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




multithreading concurrency (6)

एक एकल श्रेणी के भीतर, स्थिर फ़ील्ड हमेशा साझा किए जाते हैं। डेटा को थ्रेड में स्पष्ट रूप से दायरा देने के लिए, आप ThreadLocal जैसी सुविधा का उपयोग करना चाहेंगे।

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

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

अनिवार्य रूप से, उन्होंने कहा कि यह संभव है कि मुख्य धागे में 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 घोषित नहीं किया जाता है या लिखने को स्पष्ट रूप से सिंक्रनाइज़ किया जाता है।


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

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

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


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

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

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

private static boolean ready;  
private static int number;  

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

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


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

public static int i ;

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







memory-visibility