आपका मुहावरा सुरक्षित है यदि और केवल यदि के संदर्भ में HashMap
है सुरक्षित रूप से प्रकाशित । HashMap
स्वयं के आंतरिक से संबंधित कुछ के बजाय , सुरक्षित प्रकाशन इस बात से संबंधित है कि निर्माण धागा अन्य थ्रेड्स के लिए दिखाई देने वाले नक्शे का संदर्भ कैसे बनाता है।
मूल रूप से, यहां एकमात्र संभव दौड़ निर्माण HashMap
और किसी भी पढ़ने वाले धागे के बीच है जो पूरी तरह से निर्माण से पहले इसे एक्सेस कर सकता है। अधिकांश चर्चा नक्शा ऑब्जेक्ट की स्थिति के साथ क्या होता है, लेकिन यह अप्रासंगिक है क्योंकि आप इसे कभी भी संशोधित नहीं करते हैं - इसलिए केवल दिलचस्प हिस्सा यह है कि HashMap
संदर्भ कैसे प्रकाशित किया जाता है।
उदाहरण के लिए, कल्पना करें कि आप मानचित्र को इस तरह प्रकाशित करते हैं:
class SomeClass {
public static HashMap<Object, Object> MAP;
public synchronized static setMap(HashMap<Object, Object> m) {
MAP = m;
}
}
... और किसी बिंदु पर setMap()
एक नक्शे के साथ कॉल किया जाता है, और अन्य धागे SomeClass.MAP
मानचित्र का उपयोग करने के लिए उपयोग कर रहे हैं , और इस तरह अशक्त के लिए जाँच करें:
HashMap<Object,Object> map = SomeClass.MAP;
if (map != null) {
.. use the map
} else {
.. some default behavior
}
यह सुरक्षित नहीं है , हालांकि यह संभवतः ऐसा प्रतीत होता है जैसे यह है। समस्या यह है कि किसी दूसरे धागे पर सेट और उसके बाद के पढ़ने के बीच कोई संबंध नहीं हैSomeObject.MAP
, इसलिए आंशिक रूप से निर्मित मानचित्र देखने के लिए रीडिंग थ्रेड स्वतंत्र है। यह बहुत कुछ भी कर सकता है और यहां तक कि व्यवहार में भी यह करता है कि रीडिंग थ्रेड को अनंत लूप में डाल दें ।
सुरक्षित रूप से नक्शे को प्रकाशित करने के लिए आपको स्थापित करने के लिए एक की जरूरत है क्या होता है-पहले के बीच संबंधों को संदर्भ के लेखन के लिए HashMap
(यानी, प्रकाशन ) और कहा कि संदर्भ (यानी, खपत) के बाद के पाठकों। आसानी से, याद रखने के कुछ ही आसान तरीके हैं जिन्हें पूरा करने के लिए [1] :
- एक ठीक से बंद क्षेत्र के माध्यम से संदर्भ का आदान-प्रदान करें ( JLS 17.4.5 )
- इनीशियल स्टोर करने के लिए स्टैटिक इनिशियलाइज़र का उपयोग करें ( JLS 12.4 )
- वाष्पशील क्षेत्र ( JLS 17.4.5 ) के माध्यम से या इस नियम के परिणाम के रूप में एटोमिकएक्स कक्षाओं के माध्यम से संदर्भ का आदान-प्रदान करें।
- मूल्य को एक अंतिम क्षेत्र ( जेएलएस 17.5 ) में प्रारंभ करें ।
आपके परिदृश्य के लिए सबसे दिलचस्प हैं (2), (3) और (4)। विशेष रूप से, (3) मेरे पास दिए गए कोड पर सीधे लागू होता है: यदि आप की घोषणा को बदल MAP
देते हैं:
public static volatile HashMap<Object, Object> MAP;
फिर सब कुछ कोषेर है: जो पाठक गैर-शून्य मान देखते हैं, उनके पास स्टोर के साथ संबंध होने से पहले जरूरी होता है MAP
और इसलिए मैप इनिशियलाइज़ेशन से जुड़े सभी स्टोर देखें।
अन्य विधियां आपके तरीके के शब्दार्थ को बदल देती हैं, क्योंकि दोनों (2) (स्थैतिक initalizer का उपयोग करके) और (4) ( अंतिम का उपयोग करके ) का अर्थ है कि आप MAP
रनटाइम पर गतिशील रूप से सेट नहीं कर सकते हैं । यदि आपको ऐसा करने की आवश्यकता नहीं है, तो बस MAP
एक के रूप में घोषित करें static final HashMap<>
और आपको सुरक्षित प्रकाशन की गारंटी दी जाती है।
व्यवहार में, "कभी नहीं संशोधित वस्तुओं" के लिए सुरक्षित पहुँच के लिए नियम सरल हैं:
यदि आप एक ऐसी वस्तु प्रकाशित कर रहे हैं जो स्वाभाविक रूप से अपरिवर्तनीय नहीं है (जैसा कि सभी क्षेत्रों में घोषित किया गया है final
) और:
- आप पहले से ही उस वस्तु का निर्माण कर सकते हैं जिसे घोषणा के क्षण में सौंपा जाएगा : a (केवल स्थैतिक सदस्यों के लिए)
final
फ़ील्ड का उपयोग करें static final
।
- आप ऑब्जेक्ट को बाद में असाइन करना चाहते हैं, संदर्भ पहले से ही दिखाई देने के बाद: एक अस्थिर फ़ील्ड का उपयोग करें b ।
बस!
व्यवहार में, यह बहुत कुशल है। एक static final
क्षेत्र का उपयोग , उदाहरण के लिए, जेवीएम को मानने की अनुमति देता है कि कार्यक्रम के जीवन के लिए अपरिवर्तित है और इसे भारी रूप से अनुकूलित करें। एक के उपयोग के final
सदस्य क्षेत्र की अनुमति देता है सबसे आर्किटेक्चर एक सामान्य क्षेत्र पढ़ने के लिए एक रास्ता बराबर में क्षेत्र को पढ़ने के लिए और नहीं रोकना आगे अनुकूलन करता है ग ।
अंत में, के उपयोग का volatile
कुछ प्रभाव पड़ता है: कई आर्किटेक्चर पर कोई हार्डवेयर अवरोध की आवश्यकता नहीं होती है (जैसे कि x86, विशेष रूप से वे जो रीड को पास करने की अनुमति नहीं देते हैं), लेकिन संकलन के समय कुछ अनुकूलन और पुनरावृत्ति नहीं हो सकती है - लेकिन यह प्रभाव आमतौर पर छोटा होता है। बदले में, आप वास्तव में जितना मांगते हैं उससे अधिक प्राप्त करते हैं - न केवल आप सुरक्षित रूप से प्रकाशित HashMap
कर सकते हैं, आप उतने ही अधिक संशोधित नहीं स्टोर कर सकते हैं HashMap
जितना आप उसी संदर्भ में चाहते हैं और आश्वस्त रहें कि सभी पाठक सुरक्षित रूप से प्रकाशित मानचित्र देखेंगे ।
अधिक विवरण के लिए, मैनसिल और गोएत्ज़ द्वारा शिपिलेव या इस FAQ को देखें ।
[१] सीधे शिपिलेव से उद्धृत ।
यह एक जटिल लगता है, लेकिन मेरा मतलब यह है कि आप निर्माण के समय संदर्भ निर्दिष्ट कर सकते हैं - या तो घोषणा बिंदु पर या निर्माणकर्ता (सदस्य क्षेत्र) या स्थिर आरम्भिक (स्थैतिक क्षेत्र) में।
बी वैकल्पिक रूप से, आप synchronized
प्राप्त करने / सेट करने के लिए एक विधि का उपयोग कर सकते हैं , या एक AtomicReference
या कुछ और कर सकते हैं, लेकिन हम उस न्यूनतम कार्य के बारे में बात कर रहे हैं जो आप कर सकते हैं।
सी कुछ आर्किटेक्चर बहुत कमजोर मेमोरी मॉडल के साथ (मैं आपको देख रहा हूं , अल्फा) एक final
पढ़ने से पहले कुछ प्रकार के रीड बैरियर की आवश्यकता हो सकती है - लेकिन ये आज बहुत दुर्लभ हैं।