क्या विभिन्न कुंजियों के लिए एक HashMap धागा सुरक्षित है?


87

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

जवाबों:


99

@ डॉट्सिड के उत्तर में वह यह कहता है:

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

वह सही है। सिंक्रनाइज़ेशन के बिना अपडेट किया गया एक HashMap टूट जाएगा, भले ही थ्रेड कुंजियों के निराशाजनक सेट का उपयोग कर रहे हों। यहाँ कुछ चीजें हैं जो गलत हो सकती हैं।

  • यदि एक धागा ए करता है put, तो एक और धागा हैशमैप के आकार के लिए एक बासी मान देख सकता है।

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

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

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

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

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

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

30

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

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

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

हालांकि यह दृश्यता के बारे में कोई गारंटी नहीं देता है। इसलिए आपको कभी-कभार बासी संघों को पुनः प्राप्त करने के लिए तैयार रहना होगा।


6

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

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

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

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


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

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

जैसा कि मैंने पहले ही बताया था: "जिन वस्तुओं को हाशप द्वारा इंगित किया जाता है, उन्हें भी नहीं बदलना चाहिए"
डेनिस बज़्हेनोव

4

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

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

1
यह सिर्फ दौड़ की स्थिति से भी बदतर है। HashMapआपके द्वारा उपयोग किए जा रहे कार्यान्वयन के आंतरिक आधार पर , आप HashMapडेटा संरचनाओं का भ्रष्टाचार प्राप्त कर सकते हैं , स्मृति विसंगतियों के कारण वगैरह।
स्टीफन सी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.