क्या कई थ्रेड्स (कोई संशोधन) से java.util.HashMap से मान प्राप्त करना सुरक्षित है?


138

एक ऐसा मामला है जहां एक मानचित्र का निर्माण किया जाएगा, और एक बार इसे शुरू करने के बाद, इसे फिर से संशोधित नहीं किया जाएगा। हालाँकि, इसे कई थ्रेड्स से (केवल (कुंजी) प्राप्त करें) एक्सेस किया जा सकता है। क्या java.util.HashMapइस तरह से उपयोग करना सुरक्षित है ?

(वर्तमान में, मैं खुशी से एक का उपयोग कर रहा java.util.concurrent.ConcurrentHashMapहै, और प्रदर्शन में सुधार करने के लिए कोई मापा की जरूरत है, लेकिन बस उत्सुक अगर एक साधारण हूँ HashMapपर्याप्त होगा। इसलिए, इस सवाल यह है कि नहीं "एक कौन सा उपयोग करना चाहिए?" और न ही यह एक प्रदर्शन सवाल है। बल्कि, सवाल यह है कि "क्या यह सुरक्षित होगा?")


4
यहां कई उत्तर थ्रेडिंग से आपसी बहिष्कार के बारे में सही हैं, लेकिन मेमोरी अपडेट के बारे में गलत है। मैंने तदनुसार मतदान किया है, लेकिन सकारात्मक मतों के साथ अभी भी कई गलत उत्तर हैं।
हीथ बॉर्डर्स

@ नीचे सीमाएं, अगर उदाहरण एक स्टेटिकली अनअमोडिफायबल हाशप को इनिशियलाइज़ किया गया था, तो यह समवर्ती रीड के लिए सुरक्षित होना चाहिए (क्योंकि अन्य थ्रेड्स अपडेट अपडेट नहीं हो सकते थे क्योंकि अपडेट नहीं थे), है ना?
काकाकाओ

यदि इसे स्टैटिकली ब्लॉक के बाहर स्टैटिकली इनिशियलाइज़ किया गया है और कभी संशोधित नहीं किया गया है, तो यह ठीक हो सकता है क्योंकि सभी स्टैटिक इनिशियलाइज़ेशन को सिंक्रनाइज़ किया जाता है ClassLoader। यह अपने आप में एक अलग प्रश्न है। मैं अभी भी स्पष्ट रूप से यह सत्यापित करने के लिए प्रोफ़ाइल और यह सत्यापित करूंगा कि यह वास्तविक प्रदर्शन समस्याओं का कारण बन रहा था।
हीथ बॉर्डर्स

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

2
@ बताइए - मुझे लगता है कि आप 8 साल बाद भी उत्तर की तलाश में नहीं हैं, लेकिन रिकॉर्ड के लिए, लगभग सभी उत्तरों में मुख्य भ्रम यह है कि वे उन कार्यों पर ध्यान केंद्रित करते हैं जो आप मानचित्र ऑब्जेक्ट पर लेते हैं । आप पहले ही समझा चुके हैं कि आप कभी भी वस्तु को संशोधित नहीं करते हैं, इसलिए यह सब अप्रासंगिक है। एकमात्र संभावित "गोच" तब आप संदर्भ को कैसे प्रकाशित करते हैं Map, यह आपने नहीं बताया। यदि आप इसे सुरक्षित रूप से नहीं करते हैं, तो यह सुरक्षित नहीं है। यदि आप इसे सुरक्षित रूप से करते हैं, तो यह है । मेरे उत्तर में विवरण।
21

जवाबों:


55

आपका मुहावरा सुरक्षित है यदि और केवल यदि के संदर्भ में 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] :

  1. एक ठीक से बंद क्षेत्र के माध्यम से संदर्भ का आदान-प्रदान करें ( JLS 17.4.5 )
  2. इनीशियल स्टोर करने के लिए स्टैटिक इनिशियलाइज़र का उपयोग करें ( JLS 12.4 )
  3. वाष्पशील क्षेत्र ( JLS 17.4.5 ) के माध्यम से या इस नियम के परिणाम के रूप में एटोमिकएक्स कक्षाओं के माध्यम से संदर्भ का आदान-प्रदान करें।
  4. मूल्य को एक अंतिम क्षेत्र ( जेएलएस 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पढ़ने से पहले कुछ प्रकार के रीड बैरियर की आवश्यकता हो सकती है - लेकिन ये आज बहुत दुर्लभ हैं।


never modify HashMapमेरा मतलब है state of the map objectकि धागा सुरक्षित नहीं है । ईश्वर पुस्तकालय कार्यान्वयन को जानता है, यदि आधिकारिक दस्तावेज ने यह नहीं कहा कि यह धागा सुरक्षित है।
जियांग वाईडी

@JiangYD - आप सही कह रहे हैं कि कुछ मामलों में वहाँ एक ग्रे क्षेत्र है: जब हम कहते हैं कि "संशोधित" का हमारा वास्तव में मतलब है कोई क्रिया जो आंतरिक रूप से कुछ लिखता है जो पढ़ सकता है या अन्य थ्रेड्स पर लिख सकता है। ये लिखते हैं कि आंतरिक कार्यान्वयन विवरण हो सकता है, इसलिए यहां तक ​​कि ऐसा ऑपरेशन जो "केवल पढ़ने के लिए" लगता है जैसे get()वास्तव में कुछ लेखन कर सकते हैं, कुछ आंकड़े अपडेट कर सकते हैं (या एक्सेस-ऑर्डर LinkedHashMapअपडेट करने के आदेश के मामले में )। तो एक अच्छी तरह से लिखा वर्ग कुछ प्रलेखन प्रदान करना चाहिए जो यह स्पष्ट करता है कि अगर ...
BeeOnRope

... जाहिरा तौर पर "केवल पढ़ने के लिए" ऑपरेशन वास्तव में आंतरिक रूप से केवल थ्रेड-सुरक्षा अर्थों में पढ़े जाते हैं। उदाहरण के लिए C ++ मानक लाइब्रेरी में, एक कंबल नियम है जिसे चिह्नित constकिए गए सदस्य फ़ंक्शन सही मायने में केवल इस तरह से पढ़ते हैं (आंतरिक रूप से, वे अभी भी लिख सकते हैं, लेकिन इन्हें थ्रेड-सुरक्षित बनाना होगा)। constजावा में कोई कीवर्ड नहीं है और मुझे किसी भी दस्तावेज की कंबल गारंटी के बारे में पता नहीं है, लेकिन सामान्य तौर पर मानक पुस्तकालय कक्षाएं अपेक्षा के अनुरूप व्यवहार करती हैं, और अपवादों का दस्तावेजीकरण किया जाता है ( LinkedHashMapउदाहरण देखें कि जहां आरओ ऑप्स getस्पष्ट रूप से असुरक्षित के रूप में उल्लिखित हैं)।
BeeRRope

@JiangYD - अंत में, अपने मूल प्रश्न पर वापस जा रहे हैं, क्योंकि HashMapहमारे पास वास्तव में इस वर्ग के लिए थ्रेड-सेफ्टी बिहेवियर के दस्तावेज़ीकरण का अधिकार है : यदि कई थ्रेड्स हैश मैप को समवर्ती रूप से एक्सेस करते हैं, और कम से कम एक थ्रेड मैप को संरचनात्मक रूप से संशोधित करता है, इसे बाहरी रूप से सिंक्रनाइज़ किया जाना चाहिए। (एक संरचनात्मक संशोधन एक ऐसा ऑपरेशन है जो एक या एक से अधिक मैपिंग को जोड़ता है या हटाता है; केवल एक कुंजी के साथ जुड़ा हुआ मूल्य बदल रहा है जिसमें एक उदाहरण पहले से ही एक संरचनात्मक संशोधन नहीं है।)
BeeOnRope

इसलिए उन HashMapतरीकों के लिए जिन्हें हम केवल पढ़ने के लिए उम्मीद करते हैं, केवल पढ़ने के लिए हैं, क्योंकि वे संरचनात्मक रूप से संशोधित नहीं करते हैं HashMap। बेशक, यह गारंटी अन्य Mapकार्यान्वयन के लिए मनमानी नहीं कर सकती है , लेकिन सवाल HashMapविशेष रूप से है।
BeeOnRope

70

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

विषय पर अधिक जानकारी के लिए, जेरेमी के ब्लॉग पोस्ट पढ़ें:

जावा में अपरिवर्तनीयता पर भाग 1: http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html

जावा में अपरिवर्तनीयता पर भाग 2: http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-2.html

जावा में अपरिवर्तनीयता पर भाग 3: http://jeremymanson.blogspot.com/2008/07/immutability-in-java-part-3.html


3
यह एक अच्छा बिंदु है, लेकिन मैं स्थैतिक आरंभ पर भरोसा कर रहा हूं, जिसके दौरान कोई संदर्भ नहीं बचता है, इसलिए यह सुरक्षित होना चाहिए।
डेव एल।

5
मैं यह देखने में विफल हूं कि यह कैसे उच्च श्रेणी का उत्तर है (या उत्तर भी)। यह एक के लिए भी नहीं है, यहां तक ​​कि सवाल का जवाब भी देता है और इसमें एक प्रमुख सिद्धांत का उल्लेख नहीं है जो यह तय करेगा कि यह सुरक्षित है या नहीं: सुरक्षित प्रकाशन । "जवाब" "यह मुश्किल है" के लिए नीचे फोड़े और यहां तीन (जटिल) लिंक हैं जिन्हें आप पढ़ सकते हैं।
मधुमक्खी पालन

वह पहले वाक्य के अंत में प्रश्न का उत्तर देता है। उत्तर होने के संदर्भ में, वह इस बात को उठा रहा है कि अपरिवर्तनीयता (प्रश्न के पहले पैराग्राफ में बताई गई) सीधी नहीं है, साथ ही मूल्यवान संसाधन भी हैं जो उस विषय को आगे समझाते हैं। अंक यह मापते हैं कि क्या यह एक उत्तर है, यह मापता है कि क्या उत्तर दूसरों के लिए "उपयोगी" था। उत्तर को स्वीकार किया जा रहा है इसका मतलब है कि यह वह उत्तर था जिसकी ओपी तलाश कर रहा था, जिसे आपका उत्तर प्राप्त हुआ।
जेसी

@ जेसे वह पहले वाक्य के अंत में सवाल का जवाब नहीं दे रहा है, वह सवाल का जवाब दे रहा है "क्या यह एक अपरिवर्तनीय वस्तु तक पहुंचने के लिए सुरक्षित है", जो ओपी के प्रश्न पर लागू नहीं हो सकता है या नहीं क्योंकि वह अगले वाक्य में इंगित करता है। अनिवार्य रूप से यह लगभग एक लिंक-ओनली "गो फिगर इटसेल्फ" टाइप उत्तर है, जो एसओ के लिए एक अच्छा जवाब नहीं है। के रूप में upvotes के लिए, मुझे लगता है कि यह 10.5 वर्ष पुराना होने और अक्सर खोजे जाने वाले विषय का अधिक कार्य है। इसने पिछले कई वर्षों में केवल बहुत कम शुद्ध उत्थान प्राप्त किया है, इसलिए शायद लोग चारों ओर आ रहे हैं :)।
मधुमक्खी पालन

35

रीडिंग्स एक सिंक्रनाइज़ेशन दृष्टिकोण से सुरक्षित हैं, लेकिन मेमोरी के दृष्टिकोण से नहीं। यह एक ऐसी चीज़ है जो स्टैकओवरफ़्लो पर जावा डेवलपर्स के बीच व्यापक रूप से गलतफहमी है। ( प्रमाण के लिए इस उत्तर की रेटिंग पर गौर करें ।)

यदि आपके पास अन्य थ्रेड चल रहे हैं, तो हो सकता है कि वे हैंशपॉप की अपडेटेड कॉपी न देखें यदि वर्तमान थ्रेड के बाहर कोई मेमोरी राइट नहीं है। मेमोरी लिखी गई सिंक्रनाइज़ या वाष्पशील कीवर्ड के उपयोग के माध्यम से होती है, या कुछ जावा कंसर्ट के निर्माण के उपयोग के माध्यम से होती है।

देखें नया जावा मेमोरी मॉडल पर ब्रायन गोएज़ के लेख जानकारी के लिए।


दोहरी प्रविष्टि हीथ के लिए क्षमा करें, मैंने मुझे प्रस्तुत करने के बाद केवल आपका ध्यान दिया। :)
अलेक्जेंडर

2
मुझे खुशी है कि यहां अन्य लोग हैं जो वास्तव में स्मृति-प्रभावों को समझते हैं।
हीथ बॉर्डर्स

1
वास्तव में, हालांकि कोई भी धागा ऑब्जेक्ट को ठीक से प्रारंभ करने से पहले नहीं देखेगा, इसलिए मुझे नहीं लगता कि इस मामले में कोई चिंता है।
डेव एल।

1
यह पूरी तरह से इस बात पर निर्भर करता है कि ऑब्जेक्ट को कैसे आरम्भ किया जाता है।
बिल माइकेल

1
प्रश्न कहता है कि एक बार हाशप को आरम्भ करने के बाद, वह इसे और अपडेट करने का इरादा नहीं रखता है। तब से, वह केवल इसे केवल-पढ़ने के लिए डेटा संरचना के रूप में उपयोग करना चाहता है। मुझे लगता है, ऐसा करना सुरक्षित होगा, बशर्ते, उसके मानचित्र में संग्रहीत डेटा अपरिवर्तनीय हो।
बिनीता भारती

9

थोड़ा और देखने के बाद, मैंने जावा डॉक (जोर मेरा) में यह पाया :

ध्यान दें कि यह कार्यान्वयन सिंक्रनाइज़ नहीं है। यदि कई थ्रेड्स हैश मैप को समवर्ती रूप से एक्सेस करते हैं, और थ्रेड्स में से कम से कम एक मानचित्र को संरचनात्मक रूप से संशोधित करता है, तो इसे बाहरी रूप से सिंक्रनाइज़ किया जाना चाहिए। (एक संरचनात्मक संशोधन किसी भी ऑपरेशन को जोड़ता है या एक या एक से अधिक मैपिंग को हटाता है; केवल एक कुंजी से जुड़ा मूल्य बदल रहा है जिसमें एक उदाहरण पहले से ही है एक संरचनात्मक संशोधन नहीं है।)

इससे यह प्रतीत होता है कि यह सुरक्षित होगा, यह मानते हुए कि कथन का सत्य है।


1
हालांकि यह उत्कृष्ट सलाह है, अन्य उत्तर के रूप में, एक अपरिवर्तनीय, सुरक्षित रूप से प्रकाशित मानचित्र उदाहरण के मामले में अधिक सूक्ष्म उत्तर है। लेकिन आपको यह करना चाहिए कि यदि आप जानते हैं कि आप क्या कर रहे हैं।
एलेक्स मिलर

1
उम्मीद है कि इस तरह के सवालों से हमें पता चल सकता है कि हम क्या कर रहे हैं।
डेव एल।

यह वास्तव में सही नहीं है। अन्य उत्तरों की स्थिति के अनुसार, अंतिम संशोधन और बाद के सभी "थ्रेड सेफ" रीड के बीच एक -पहले होना चाहिए । आम तौर पर इसका मतलब है कि आपको वस्तु को सुरक्षित रूप से प्रकाशित करने के बाद इसे बनाना चाहिए और इसके संशोधन किए जाने चाहिए। पहला, चिह्नित सही उत्तर देखें।
मार्कस्पेस

9

एक नोट यह है कि कुछ परिस्थितियों में, एक असम्बद्ध सिंक्रनाइज़ हाशप से प्राप्त () अनंत लूप का कारण बन सकता है। यह तब हो सकता है जब एक समवर्ती पुट () मानचित्र का एक कारण बनता है।

http://lightbody.net/blog/2005/07/hashmapget_can_cause_an_infini.html


1
वास्तव में मैंने सीपीयू का उपभोग किए बिना इस जेवीएम को लटका दिया है (जो कि शायद बदतर है)
पीटर लॉरी

2
मुझे लगता है कि इस कोड को फिर से लिखा गया है ताकि अनंत लूप प्राप्त करना संभव न हो। लेकिन आप अभी भी अन्य कारणों के लिए एक असंबद्ध HashMap से हो रही है और डाल नहीं होना चाहिए।
एलेक्स मिलर 20

@AlexMiller अन्य कारणों से भी अलग है (मुझे लगता है कि आप सुरक्षित प्रकाशन की बात कर रहे हैं), मुझे नहीं लगता कि कार्यान्वयन में बदलाव के प्रतिबंध को ढीला करने का एक कारण होना चाहिए, जब तक कि इसे दस्तावेज़ द्वारा स्पष्ट रूप से अनुमति नहीं दी गई हो। ऐसा होता है, HashMap जावाडोक जावा 8 के लिए अभी भी इस चेतावनी में शामिल हैं:Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.
shmosel

8

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


मुझे यकीन नहीं है कि इसे एक्सेस करने वाला धागा किसी भी लॉक का अधिग्रहण करेगा, लेकिन मुझे यकीन है कि उन्हें ऑब्जेक्ट के लिए एक संदर्भ नहीं मिलेगा, जब तक कि इसे शुरू नहीं किया गया है, इसलिए मुझे नहीं लगता कि उनके पास एक बासी प्रतिलिपि हो सकती है।
डेव एल।

@ एलेक्स: हैशपे का संदर्भ समान मेमोरी विजिबिलिटी गारंटी बनाने के लिए अस्थिर हो सकता है। @Dave: यह है नई objs के लिए संदर्भ को देखने के लिए पहले अपने ctor का काम अपने धागा करने के लिए दिखाई देने लगता है संभव।
क्रिस वेस्ट

@ क्रिसियन सामान्य मामले में, निश्चित रूप से। मैं कह रहा था कि इस कोड में, यह नहीं है।
डेव एल।

एक रैंडम लॉक को प्राप्त करने से पूरे थ्रेड सीपीयू कैश को क्लीयर नहीं किया जा सकता है। यह जेवीएम कार्यान्वयन पर निर्भर करता है, और यह इस तरह से नहीं होने की संभावना है।
पियरे

मैं पियरे से सहमत हूं, मुझे नहीं लगता कि किसी भी लॉक को प्राप्त करना पर्याप्त होगा। परिवर्तनों को दृश्यमान होने के लिए आपको उसी लॉक पर सिंक्रनाइज़ करना होगा।
दामलु

5

Http://www.ibm.com/developerworks/java/library/j-jtp03304/ के अनुसार # इनिशियलाइज़ेशन सेफ्टी के आप अपने हशप को अंतिम क्षेत्र बना सकते हैं और कंस्ट्रक्टर के खत्म होने के बाद इसे सुरक्षित रूप से प्रकाशित किया जाएगा।

... नए मेमोरी मॉडल के तहत, एक कंस्ट्रक्टर में अंतिम फ़ील्ड के लेखन और दूसरे ऑब्जेक्ट में उस ऑब्जेक्ट के लिए एक साझा संदर्भ के शुरुआती लोड के बीच होने वाले संबंध के समान कुछ होता है। ...


यह उत्तर निम्न गुणवत्ता वाला है, यह @taylor guthier के उत्तर के समान है लेकिन कम विवरण के साथ है।
Snicolas

1
उम्म्म्म… गांड नहीं बनना है, लेकिन आपके पास तो पीछे की तरफ है। टेलर ने कहा "नहीं, इस ब्लॉग पोस्ट को देखें, उत्तर आपको आश्चर्यचकित कर सकता है", जबकि यह उत्तर वास्तव में कुछ नया जोड़ता है जो मुझे नहीं पता था ... एक के बारे में होता है-एक अंतिम क्षेत्र के लेखन के पहले संबंध निर्माता। यह उत्तर उत्कृष्ट है, और मुझे खुशी है कि मैंने इसे पढ़ा।
अजाक्स

है ना? यह एकमात्र सही उत्तर है जो मुझे उच्च श्रेणी निर्धारण उत्तरों में स्क्रॉल करने के बाद मिला। कुंजी को सुरक्षित रूप से प्रकाशित किया गया है और यह एकमात्र उत्तर है जो यहां तक ​​कि इसका उल्लेख करता है।
मधुमक्खी पालन

3

तो आपके द्वारा वर्णित परिदृश्य यह है कि आपको मानचित्र में डेटा का एक गुच्छा रखने की आवश्यकता है, फिर जब आप इसे आबाद कर रहे हैं तो आप इसे अपरिवर्तनीय मानते हैं। एक दृष्टिकोण जो "सुरक्षित" है (जिसका अर्थ है कि आप इसे लागू कर रहे हैं कि इसे वास्तव में अपरिवर्तनीय माना जाता है) संदर्भ को बदलने के लिए है Collections.unmodifiableMap(originalMap)जब आप इसे अपरिवर्तनीय बनाने के लिए तैयार हों।

उदाहरण के लिए, यदि समवर्ती रूप से उपयोग किए जाने पर कितनी बुरी तरह विफल हो सकते हैं, और मेरे द्वारा सुझाए गए वर्कअराउंड, इस बग परेड प्रविष्टि की जाँच करें: bug_id = 6423457


2
यह "सुरक्षित" है जिसमें यह अपरिवर्तनीयता को लागू करता है, लेकिन यह थ्रेड सुरक्षा समस्या को संबोधित नहीं करता है। यदि नक्शा अनमॉडिफ़िबिलिटी रैपर के साथ उपयोग करने के लिए सुरक्षित है तो इसके बिना सुरक्षित है, और इसके विपरीत।
डेव एल।

2

यह सवाल ब्रायन गोएत्ज़ की "जावा कंसेंटर इन प्रैक्टिस" पुस्तक (16.8 लिस्टिंग, पृष्ठ 350) में संबोधित किया गया है:

@ThreadSafe
public class SafeStates {
    private final Map<String, String> states;

    public SafeStates() {
        states = new HashMap<String, String>();
        states.put("alaska", "AK");
        states.put("alabama", "AL");
        ...
        states.put("wyoming", "WY");
    }

    public String getAbbreviation(String s) {
        return states.get(s);
    }
}

चूँकि statesइसे घोषित किया गया है finalऔर इसके आरंभ को मालिक के वर्ग निर्माता के भीतर पूरा किया गया है, कोई भी धागा जो बाद में इस नक्शे को पढ़ता है, उसे यह देखने की गारंटी दी जाती है कि यह समय उस समय तक खत्म हो जाएगा जब तक कि कोई अन्य धागा मानचित्र की सामग्री को संशोधित करने का प्रयास नहीं करेगा।


1

चेतावनी दी जाती है कि एकल-थ्रेडेड कोड में भी, HashMap के साथ एक समवर्ती HashMap की जगह सुरक्षित नहीं हो सकता है। ConcurrentHashMap एक कुंजी या मान के रूप में शून्य को मना करता है। HashMap उन्हें मना नहीं करता है (मत पूछो)।

तो इस स्थिति में कि आपका मौजूदा कोड सेटअप के दौरान संग्रह में एक अशक्तता जोड़ सकता है (संभवतः किसी प्रकार की विफलता के मामले में), संग्रह को बदलने के रूप में वर्णित कार्यात्मक व्यवहार को बदल देगा।

उस ने कहा, बशर्ते कि आप कुछ भी न करें समवर्ती पढ़ता है एक HashMap सुरक्षित हैं।

[संपादित करें: "समवर्ती रीड्स" द्वारा, मेरा मतलब है कि समवर्ती संशोधन भी नहीं हैं।

अन्य उत्तर बताते हैं कि यह कैसे सुनिश्चित किया जाए। एक तरीका यह है कि नक्शे को अपरिवर्तनीय बनाया जाए, लेकिन यह आवश्यक नहीं है। उदाहरण के लिए, JSR133 मेमोरी मॉडल स्पष्ट रूप से एक थ्रेड शुरू करने को परिभाषित करता है एक सिंक्रनाइज़ एक्शन, जिसका अर्थ है कि थ्रेड ए में बदलाव से पहले थ्रेड बी शुरू होता है थ्रेड बी में दिखाई देते हैं।

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


चेतावनी के लिए धन्यवाद, लेकिन अशक्त कुंजियों या मूल्यों का उपयोग करने का कोई प्रयास नहीं है।
डेव एल।

सोचा कि वहाँ नहीं होगा। संग्रह में नल जावा के एक पागल कोने हैं।
स्टीव जेसप

मैं इस जवाब से सहमत नहीं हूं। "अपने आप में एक हाशपॅप सुरक्षित है" से समवर्ती पठन गलत है। यह निर्दिष्ट नहीं करता है कि क्या रीड्स एक ऐसे मानचित्र के खिलाफ हो रहे हैं जो परस्पर या अपरिवर्तनीय है। सही होने के लिए इसे पढ़ना चाहिए "समवर्ती हैशपॉट से समवर्ती पठन सुरक्षित हैं"
टेलर गौटियर

2
आपके द्वारा खुद से जुड़े लेखों के अनुसार नहीं: आवश्यकता इस बात की है कि नक्शा नहीं बदला जाना चाहिए (और पिछले बदलाव सभी पाठक सूत्र को दिखाई देने चाहिए), न कि यह अपरिवर्तनीय (जो जावा में एक तकनीकी शब्द है और एक है) पर्याप्त लेकिन सुरक्षा के लिए आवश्यक शर्त नहीं)।
स्टीव जेसप

इसके अलावा एक नोट ... एक वर्ग को प्रारंभिक रूप से एक ही लॉक पर सिंक्रनाइज़ करना (हाँ, आप स्थैतिक क्षेत्र के शुरुआती में गतिरोध कर सकते हैं), इसलिए यदि आपका प्रारंभ वैधानिक रूप से होता है, तो प्रारंभ के पूरा होने से पहले किसी और के लिए इसे देखना असंभव होगा, जैसा कि उन्हें एक ही लॉक पर अधिग्रहीत ClassLoader.loadClass पद्धति में अवरुद्ध करना होगा ... और यदि आप एक ही क्षेत्र के विभिन्न प्रतियों के बारे में सोच रहे हैं, तो आप सही होंगे ... लेकिन यह ऑर्थोगोनल होगा दौड़ की स्थिति की धारणा; क्लास लोडर के स्थिर क्षेत्र एक मेमोरी बाड़ साझा करते हैं।
अजाक्स

0

http://www.docjar.com/html/api/java/util/HashMap.java.html

यहाँ HashMap के लिए स्रोत है। जैसा कि आप बता सकते हैं, वहाँ कोई लॉकिंग / म्यूटेक्स कोड नहीं है।

इसका मतलब यह है कि जब एक मल्टीथ्रेडेड स्थिति में एक हैशपॉप से ​​पढ़ने के लिए ठीक है, तो मैं निश्चित रूप से एक समवर्ती हाशपा का उपयोग करूंगा यदि कई लेखन थे।

दिलचस्प यह है कि .NET हैशटेबल और डिक्शनरी <K, V> दोनों ने सिंक्रोनाइज़ेशन कोड में बनाया है।


2
मुझे लगता है कि ऐसी कक्षाएं हैं जहां केवल समवर्ती पढ़ने से आप परेशानी में पड़ सकते हैं, उदाहरण के लिए अस्थायी उदाहरण चर के आंतरिक उपयोग के कारण। इसलिए किसी को लॉक / म्यूटेक्स कोड के लिए एक त्वरित स्कैन से अधिक, स्रोत की सावधानीपूर्वक जांच करने की आवश्यकता है।
डेव एल।

0

यदि इनिशियलाइज़ेशन और हर पुट को सिंक्रोनाइज़ किया जाता है तो आप बच जाते हैं।

निम्न कोड सहेज रहा है क्योंकि क्लास लोडर सिंक्रनाइज़ेशन का ध्यान रखेगा:

public static final HashMap<String, String> map = new HashMap<>();
static {
  map.put("A","A");

}

निम्नलिखित कोड सहेज रहा है क्योंकि अस्थिरता के लेखन से सिंक्रनाइज़ेशन का ध्यान रखा जाएगा।

class Foo {
  volatile HashMap<String, String> map;
  public void init() {
    final HashMap<String, String> tmp = new HashMap<>();
    tmp.put("A","A");
    // writing to volatile has to be after the modification of the map
    this.map = tmp;
  }
}

यह भी काम करेगा अगर सदस्य चर अंतिम है क्योंकि अंतिम भी अस्थिर है। और अगर विधि एक निर्माता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.