2010-04-22 14 views
59

अगर मैं दो से अधिक एक HashMap तक पहुँचने धागे है, लेकिन यह गारंटी है कि वे कभी नहीं एक ही समय में एक ही कुंजी तक पहुँचने हो जाएगा, जो अभी भी एक रेस स्थिति को जन्म दे सकता है?एक हैश मैप थ्रेड-सुरक्षित विभिन्न चाबियों के लिए है?

उत्तर

72

@ dotsid के जवाब में उन्होंने यह कहते हैं:

आप तो किसी भी तरह से एक HashMap को बदलते हैं आपका कोड बस टूटा हुआ है।

वह सही है।सिंक्रनाइज़ेशन के बिना अपडेट किया गया हैश मैप भी तोड़ देगा यदि थ्रेड कुंजी के डिस्जॉइंट सेट का उपयोग कर रहे हैं। यहां कुछ ऐसी चीजें हैं जो गलत हो सकती हैं। एक धागा एक put करता

  • है, तो एक और धागा hashmap के आकार के लिए एक बासी मूल्य देख सकते हैं।

  • जब कोई थ्रेड put करता है जो तालिका के पुनर्निर्माण को ट्रिगर करता है, तो दूसरा थ्रेड हैशटेबल सरणी संदर्भ, इसके आकार, इसकी सामग्री या हैश चेन के क्षणिक या पुराने संस्करण देख सकता है। कैओस हो सकता है।

  • एक धागा एक महत्वपूर्ण है कि कुछ कुछ अन्य धागा द्वारा इस्तेमाल किया कुंजी से टकरा के लिए एक put करता है, और बाद धागा उसके प्रमुख के लिए एक put करता है, तो बाद के हैश श्रृंखला संदर्भ की पुरानी जानकारी की प्रतिलिपि देख सकते हैं। कैओस हो सकता है।

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

और अगर आप दो धागे एक साथ put या remove अनुरोध कर रही है, वहाँ दौड़ की स्थिति के लिए कई अवसर हैं।

मैं तीन समाधान के बारे में सोच सकते हैं:

  1. एक ConcurrentHashMap का प्रयोग करें।
  2. नियमित HashMap का उपयोग करें लेकिन बाहर सिंक्रनाइज़ करें; जैसे आदिम म्यूटेक्स का उपयोग करके, Lock ऑब्जेक्ट्स, इत्यादि।
  3. प्रत्येक थ्रेड के लिए एक अलग HashMap का उपयोग करें। यदि धागे में वास्तव में चाबियों का एक पृथक सेट होता है, तो उनके लिए एक मानचित्र साझा करने के लिए कोई आवश्यकता नहीं है (एल्गोरिदमिक परिप्रेक्ष्य से)। दरअसल, यदि आपके एल्गोरिदम में किसी बिंदु पर मानचित्र की चाबियाँ, मान या प्रविष्टियों को फिर से भरने वाले धागे शामिल होते हैं, तो एकल मानचित्र को कई मानचित्रों में विभाजित करने से प्रसंस्करण के उस हिस्से के लिए एक महत्वपूर्ण गति मिल सकती है।
+2

4. नियमित हैश मैप का उपयोग करें, और एक समवर्ती रीडवाइट लॉक का उपयोग करें, एकाधिक समवर्ती अनुमति देने के लिए पाठकों, लेकिन संचालन लिखने के लिए विशिष्टता। –

+2

2 का सबकेस :-) मुझे नहीं लगता कि विभिन्न तरीकों की गणना करने की आवश्यकता है कि कोई नियमित हैश मैप तक पहुंच को सिंक्रनाइज़ कर सकता है। –

+0

** सिंक्रनाइज़ कर सकते हैं ** ब्लॉक इसके लिए इस्तेमाल किया जा सकता है? –

5

यह आप "तक पहुंच" के अंतर्गत क्या मतलब है पर निर्भर करता है। यदि आप बस पढ़ रहे हैं, तो आप "happens-before" नियमों के तहत गारंटीकृत डेटा की दृश्यता तक एक ही कुंजी को पढ़ सकते हैं। इसका मतलब है कि HashMap परिवर्तन नहीं होना चाहिए और किसी भी पाठक HashMap तक पहुँचने के लिए शुरू से पहले सभी परिवर्तनों (प्रारंभिक निर्माण) पूरा किया जाना चाहिए।

आप तो किसी भी तरह से एक HashMap बदलते हैं तो आपके कोड बस टूट गया है। @ स्टीफन सी बहुत अच्छी व्याख्या क्यों प्रदान करता है।

संपादित करें: यदि पहला मामला आपकी वास्तविक स्थिति है, तो मैं आपको यह सुनिश्चित करने के लिए Collections.unmodifiableMap() का उपयोग करने की सलाह देता हूं कि आपका हैश मैप कभी नहीं बदला गया है। ऑब्जेक्ट्स जो HashMap द्वारा इंगित किए गए हैं, भी नहीं बदला जाना चाहिए, इसलिए final कीवर्ड का उपयोग करके आक्रामक कीवर्ड आपकी सहायता कर सकता है।

और जैसा कि @Lars Andren कहते हैं, ConcurrentHashMap ज्यादातर मामलों में सबसे अच्छा विकल्प है।

+0

ConcurrentHashMap के बारे में कैसे? –

+2

ConcurrentHashMap मेरी राय में सबसे अच्छा विकल्प है। एकमात्र कारण मैंने इसकी सिफारिश नहीं की, लेखक की वजह से यह नहीं पूछा :) सीएएस संचालन के कारण इसमें कम थ्रूपुट है, लेकिन समवर्ती प्रोग्रामिंग के सुनहरे नियम के अनुसार: "इसे सही बनाएं, और केवल तभी करें यह तेज़ ":) –

+0

' unmodifiableMap' सुनिश्चित करता है कि ग्राहक मानचित्र को नहीं बदल सकता है। यह सुनिश्चित करने के लिए कुछ भी नहीं करता है कि अंतर्निहित मानचित्र नहीं बदला गया है। –

24

बस एक ConcurrentHashMap का उपयोग करें। ConcurrentHashMap कई लॉक का उपयोग करता है जो लॉक होने के अवसरों को कम करने के लिए हैश बाल्टी की एक श्रृंखला को कवर करता है। एक अनचाहे लॉक प्राप्त करने के लिए एक मामूली प्रदर्शन प्रभाव है।

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

से अधिक थ्रेड एक हैश नक्शा समवर्ती एक्सेस करते हैं, और कम से कम धागे में से एक नक्शा को संशोधित करता है संरचना की दृष्टि से, इसे बाहर से सिंक्रनाइज़ किया जाना चाहिए। हालांकि यह दृश्यता के बारे में कोई गारंटी नहीं देता

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

+0

+1 नहीं बदलना चाहिए (यदि सबसे अच्छा अभ्यास नहीं है :-) – finnw

3

दो धागे से उचित सिंक्रनाइज़ेशन के बिना हैश मैप को संशोधित करना आसानी से दौड़ की स्थिति का कारण बन सकता है।

  • एक put() आंतरिक तालिका का एक आकार बदलने के लिए होता है, तो यह कुछ समय लगता है और अन्य धागा वर्ष मेज पर लिखने के लिए जारी है।
  • अलग-अलग चाबियों के लिए दो put() एक ही बाल्टी के अपडेट की ओर ले जाते हैं यदि कुंजी हैशकोड टेबल आकार के बराबर मॉड्यूल हैं। (वास्तव में, hashCode और बकेट इंडेक्स के बीच संबंध और अधिक जटिल है, लेकिन टकराव अभी भी हो सकता है।)
संबंधित मुद्दे