java - धागे के बीच स्थिर स्थिर चर हैं?




multithreading concurrency (5)

@ डोंटॉक्सटाटा आप अपने शिक्षक के पास वापस जा सकते हैं और उसे थोड़ा सा स्कूल :)

असली दुनिया से कुछ नोट्स और चाहे आप जो देखते हैं या कहा जाए। कृपया ध्यान दें, नीचे दिए गए शब्द इस विशेष मामले के बारे में दिखाए गए सटीक क्रम में हैं।

निम्नलिखित 2 चर वही कैश लाइन पर लगभग किसी भी ज्ञात वास्तुकला के अंतर्गत रहेंगे।

private static boolean ready;  
private static int number;  

धागा समूह थ्रेड हटाने (और कई अन्य मुद्दों) के कारण Thread.exit (मुख्य धागा) से बाहर exit की गारंटी है और मेमोरी बाड़ का कारण बनने की गारंटी है। (यह एक सिंक्रनाइज़ कॉल है, और मुझे सिंक भाग को लागू करने का कोई भी तरीका नहीं दिखता है क्योंकि थ्रेड ग्रुप को भी समाप्त करना होगा क्योंकि कोई डेमॉन थ्रेड छोड़ नहीं है, आदि)।

ReaderThread धागा ReaderThread थ्रेड प्रक्रिया को जीवित रखने जा रहा है क्योंकि यह एक डेमन नहीं है! इस प्रकार ready और number एक साथ फिसल जाएगी (या एक संदर्भ स्विच होने से पहले संख्या) और इस मामले में पुन: समन्वय के लिए कोई वास्तविक कारण नहीं है कम से कम मैं एक के बारे में भी सोच नहीं सकता। आपको कुछ भी देखने के लिए वास्तव में अजीब कुछ चाहिए लेकिन 42 । फिर मैं मानता हूं कि स्थैतिक चर दोनों एक ही कैश लाइन में होंगे। मैं बस एक कैश लाइन 4 बाइट्स लंबा या एक JVM कल्पना नहीं कर सकता जो उन्हें एक सतत क्षेत्र (कैश लाइन) में असाइन नहीं करेगा।

थ्रेडिंग पर ऊपरी स्तर के जावा वर्ग में मेरे शिक्षक ने कुछ ऐसा कहा जो मुझे यकीन नहीं था।

उन्होंने कहा कि निम्नलिखित कोड आवश्यक चर अद्यतन नहीं करेगा। उनके अनुसार, दो धागे अनिवार्य रूप से स्थैतिक चर साझा नहीं करते हैं, विशेष रूप से इस मामले में जब प्रत्येक धागा (मुख्य थ्रेड बनाम रीडर थ्रेड) अपने प्रोसेसर पर चल रहा है और इसलिए एक ही रजिस्ट्रार / कैश / आदि साझा नहीं करता है और एक सीपीयू दूसरे को अपडेट नहीं करेगा।

अनिवार्य रूप से, उन्होंने कहा कि यह संभव है कि मुख्य धागे में ready किया गया हो, लेकिन रीडर थ्रेड में नहीं, ताकि रीडर थ्रेड असीम रूप से लूप हो। उन्होंने यह भी दावा किया कि कार्यक्रम के लिए '0' या '42' प्रिंट करना संभव था। मैं समझता हूं कि कैसे '42' मुद्रित किया जा सकता है, लेकिन '0' नहीं। उन्होंने उल्लेख किया कि यह तब होगा जब number चर डिफ़ॉल्ट मान पर सेट हो।

मैंने सोचा कि शायद यह गारंटी नहीं है कि स्थैतिक चर धागे के बीच अद्यतन किया गया है, लेकिन यह मुझे जावा के लिए बहुत अजीब बनाता है। क्या इस समस्या को ready करने के लिए ready अस्थिर बनाना है?

उसने यह कोड दिखाया:

public class NoVisibility {  
    private static boolean ready;  
    private static int number;  
    private static class ReaderThread extends Thread {   
        public void run() {  
            while (!ready)   Thread.yield();  
            System.out.println(number);  
        }  
    }  
    public static void main(String[] args) {  
        new ReaderThread().start();  
        number = 42;  
        ready = true;  
    }  
}

असल में यह सच है, लेकिन वास्तव में समस्या अधिक जटिल है। साझा डेटा की दृश्यता न केवल सीपीयू कैश द्वारा प्रभावित की जा सकती है, बल्कि निर्देशों के निष्पादन के निष्पादन से भी प्रभावित हो सकती है।

इसलिए जावा मेमोरी मॉडल को परिभाषित करता है, जो बताता है कि किस परिस्थिति में थ्रेड साझा डेटा की लगातार स्थिति देख सकते हैं।

आपके विशेष मामले में, volatile गारंटी दृश्यता जोड़ना।


जब आप स्थैतिक आदिम प्रकार परिवर्तनीय जावा डिफ़ॉल्ट प्रारंभ करते हैं तो स्थिर चर के लिए मान निर्दिष्ट करता है

public static int i ;

जब आप इस तरह चर को परिभाषित करते हैं तो i = 0 का डिफ़ॉल्ट मान; यही कारण है कि आपको 0 प्राप्त करने की संभावना क्यों है। फिर मुख्य धागा बूलियन के मूल्य को सत्य के लिए तैयार करता है। चूंकि तैयार एक स्थिर चर, मुख्य धागा और अन्य थ्रेड संदर्भ एक ही स्मृति पते के लिए संदर्भित है ताकि तैयार परिवर्तनीय परिवर्तन हो। तो लूप और प्रिंट मान के दौरान माध्यमिक धागा बाहर निकलता है। जब मूल्य का मूल्य प्रारंभिक मान मुद्रित किया जाता है तो 0 है। थ्रेड प्रक्रिया पास हो गई है जबकि मुख्य थ्रेड अद्यतन संख्या चर से पहले लूप। तो 0 प्रिंट करने की संभावना है


वह दृश्यता के बारे में बात कर रहा था और उसे सचमुच नहीं लेना था।

स्थिर चर वास्तव में धागे के बीच साझा किए जाते हैं, लेकिन एक थ्रेड में किए गए परिवर्तन तुरंत किसी अन्य थ्रेड पर दिखाई नहीं दे सकते हैं, ऐसा लगता है कि वेरिएबल की दो प्रतियां हैं।

यह आलेख एक ऐसा दृश्य प्रस्तुत करता है जो इस जानकारी के अनुरूप है कि उसने जानकारी कैसे प्रस्तुत की:

सबसे पहले, आपको जावा मेमोरी मॉडल के बारे में कुछ समझना होगा। मैंने संक्षेप में और अच्छी तरह से व्याख्या करने के लिए सालों से थोड़ा सा संघर्ष किया है। आज तक, इसका वर्णन करने का सबसे अच्छा तरीका यह है कि यदि आप इसे इस तरह कल्पना करते हैं:

  • जावा में प्रत्येक थ्रेड एक अलग मेमोरी स्पेस में होता है (यह स्पष्ट रूप से असत्य है, इसलिए इस पर मेरे साथ भालू)।

  • आपको यह सुनिश्चित करने के लिए विशेष तंत्र का उपयोग करने की आवश्यकता है कि इन धागे के बीच संचार होता है, जैसा कि आप संदेश पास करने वाले सिस्टम पर करेंगे।

  • मेमोरी लिखती है कि एक धागे में होने पर "रिसाव" हो सकता है और किसी अन्य थ्रेड द्वारा देखा जा सकता है, लेकिन इसका कोई गारंटी नहीं है। स्पष्ट संचार के बिना, आप गारंटी नहीं दे सकते कि कौन से लेखन अन्य धागे, या यहां तक ​​कि जिस क्रम में उन्हें देखा जाता है, द्वारा देखा जाता है।

...

लेकिन फिर, यह थ्रेडिंग और अस्थिरता के बारे में सोचने के लिए बस एक मानसिक मॉडल है, सचमुच नहीं कि जेवीएम कैसे काम करता है।


स्थैतिक चर के लिए विशिष्ट कोई दृश्यता समस्या नहीं है। JVM के मेमोरी मॉडल द्वारा लगाए गए दृश्यता मुद्दे हैं। मेमोरी मॉडल के बारे में बात करते हुए एक लेख यहां लिखा गया है और धागे के लिए कैसे लिखते हैं । आप परिवर्तनों पर भरोसा नहीं कर सकते हैं, एक थ्रेड समय-समय पर अन्य थ्रेडों के लिए दृश्यमान हो रहा है (वास्तव में जेवीएम के पास उन परिवर्तनों को आपके लिए दृश्यमान करने का कोई दायित्व नहीं है), जब तक कि आप पहले से संबंधों को स्थापित न करें, यहां उद्धरण दिया गया है वह लिंक (जेड वेस्ले-स्मिथ द्वारा टिप्पणी में आपूर्ति):

जावा भाषा विशिष्टता का अध्याय 17 साझा चर के पढ़ने और लिखने जैसे मेमोरी संचालन पर होने वाले पहले संबंध को परिभाषित करता है। एक थ्रेड द्वारा लिखे जाने वाले परिणामों को पढ़ने के ऑपरेशन से पहले लिखने के ऑपरेशन होने पर ही किसी अन्य धागे द्वारा पढ़ने के लिए दृश्यमान होने की गारंटी दी जाती है। सिंक्रनाइज़ और अस्थिर संरचनाएं, साथ ही साथ थ्रेड.स्टार्ट () और थ्रेड.जॉइन () विधियां, संबंधों से पहले होती हैं। विशेष रूप से:

  • थ्रेड में प्रत्येक क्रिया होती है-उस थ्रेड में प्रत्येक क्रिया से पहले जो प्रोग्राम के क्रम में बाद में आती है।

  • एक मॉनीटर का एक अनलॉक (सिंक्रनाइज़ ब्लॉक या विधि निकास) होता है-उसी मॉनीटर के प्रत्येक बाद वाले लॉक (सिंक्रनाइज़ ब्लॉक या विधि प्रविष्टि) से पहले। और क्योंकि होता है- संबंध पहले से संक्रमित होता है, अनलॉक करने से पहले थ्रेड के सभी क्रियाएं होती हैं-किसी भी थ्रेड लॉकिंग के बाद सभी क्रियाओं से पहले।

  • एक अस्थिर क्षेत्र को लिखना-उसी क्षेत्र के बाद के पढ़ने से पहले होता है। अस्थिर क्षेत्रों के लेखन और पढ़ने में मॉनीटर में प्रवेश करने और बाहर निकलने के समान ही स्मृति स्थिरता प्रभाव होते हैं, लेकिन आपसी बहिष्करण लॉकिंग को लागू नहीं करते हैं।

  • धागे पर शुरू करने के लिए एक कॉल शुरू होता है - प्रारंभ धागे में किसी भी कार्रवाई से पहले।

  • धागे में सभी क्रियाएं होती हैं-किसी अन्य थ्रेड से पहले उस धागे पर शामिल होने से सफलतापूर्वक वापस आती है।





memory-visibility