2009-02-19 17 views
79

क्या निम्न कोड synchronizedMap पर कॉल को सही तरीके से सिंक्रनाइज़ करने के लिए सेट किया गया है?जावा सिंक्रनाइज़ ब्लॉक बनाम संग्रह। सिंक्रनाइज़ किए गए मैप

public class MyClass { 
    private static Map<String, List<String>> synchronizedMap = Collections.synchronizedMap(new HashMap<String, List<String>>()); 

    public void doWork(String key) { 
    List<String> values = null; 
    while ((values = synchronizedMap.remove(key)) != null) { 
     //do something with values 
    } 
    } 

    public static void addToMap(String key, String value) { 
    synchronized (synchronizedMap) { 
     if (synchronizedMap.containsKey(key)) { 
     synchronizedMap.get(key).add(value); 
     } 
     else { 
     List<String> valuesList = new ArrayList<String>(); 
     valuesList.add(value); 
     synchronizedMap.put(key, valuesList); 
     } 
    } 
    } 
} 

मेरी समझ से, मैं इससे पहले कि मैं put() करने के लिए कॉल के माध्यम से प्राप्त remove() या containsKey() कॉल करने से एक और धागा को रोकने के लिए addToMap() में सिंक्रनाइज़ ब्लॉक की जरूरत है, लेकिन क्योंकि एक और धागा प्रवेश नहीं कर सकते मैं doWork() में एक तुल्यकालन ब्लॉक की जरूरत नहीं है में सिंक्रनाइज़ ब्लॉक remove() से पहले लौटाता है क्योंकि मैंने मूल रूप से Collections.synchronizedMap() के साथ मानचित्र बनाया था। क्या वो सही है? क्या ऐसा करने के लिए इससे अच्छा तरीका है?

उत्तर

83

Collections.synchronizedMap() गारंटी देता है कि मानचित्र पर चलने वाले प्रत्येक परमाणु ऑपरेशन को सिंक्रनाइज़ किया जाएगा।

मानचित्र पर दो (या अधिक) संचालन चलाना, हालांकि, ब्लॉक में सिंक्रनाइज़ किया जाना चाहिए। तो हाँ - आप सही ढंग से सिंक्रनाइज़ कर रहे हैं।

+24

में मैं इसे लगता है जब यह उल्लेख करना अच्छा होगा कि यह काम करता है क्योंकि javadocs स्पष्ट रूप से बताता है कि सिंक्रनाइज़मैप मानचित्र पर ही सिंक्रनाइज़ करता है, और कुछ आंतरिक लॉक नहीं। अगर वह सिंक्रनाइज़ किया गया था (सिंक्रनाइज़मैप) सही नहीं होगा। – extraneon

+1

@ युवाल क्या आप थोड़ा सा गहराई में अपना उत्तर समझा सकते हैं? आप कहते हैं कि सिंक्रनाइज़मैप परमाणु रूप से संचालन करता है, लेकिन फिर सिंकमैप ने आपके सभी परिचालन परमाणु बनाये तो आपको कभी भी अपने सिंक्रनाइज़ किए गए ब्लॉक की आवश्यकता क्यों होगी? आपका पहला अनुच्छेद दूसरे के बारे में चिंता करने से रोकता है। – almel

+0

@almel मेरी [जवाब] को देखने के (http://stackoverflow.com/questions/567068/java-synchronized-block-vs-collections-synchronizedmap/32608663#32608663) – Sergey

14

आप JDK 6 का उपयोग कर रहे हैं तो आप की जाँच करने के लिए ConcurrentHashMap

ध्यान दें कि कक्षा में putIfAbsent विधि चाहते हो सकता है।

+8

यह वास्तव में JDK 1.5 – Bogdan

4

यह मेरे लिए सही लग रहा है। अगर मैं कुछ भी बदलना चाहता था, तो मैं Collections.synchronizedMap() का उपयोग करना बंद कर दूंगा और सबकुछ सिंक्रनाइज़ करूँगा, बस इसे स्पष्ट करने के लिए।

इसके अलावा

, मैं

List<String> valuesList = synchronziedMap.get(key); 
if (valuesList == null) 
{ 
    valuesList = new ArrayList<String>(); 
    synchronziedMap.put(key, valuesList); 
} 
valuesList.add(value); 
+2

करने के लिए बात है। मैं समझ नहीं आता कि क्यों हम 'Collections.synchronizedXXX()' एपीआई का उपयोग करना चाहिए कि हम अभी भी (जो ज्यादातर मामलों में सिर्फ संग्रह में ही हो जाएगा) किसी वस्तु पर सिंक्रनाइज़ करने के लिए हमारे हर दिन एप्लिकेशन के तर्क – kellogs

2

चेक बाहर Google Collections 'Multimap, उदा

if (synchronizedMap.containsKey(key)) { 
    synchronizedMap.get(key).add(value); 
    } 
    else { 
    List<String> valuesList = new ArrayList<String>(); 
    valuesList.add(value); 
    synchronizedMap.put(key, valuesList); 
    } 

की जगह होगी पृष्ठ 28 का this presentation

यदि आप किसी कारण से उस लाइब्रेरी का उपयोग नहीं कर सकते हैं, तो SynchronizedHashMap के बजाय ConcurrentHashMap का उपयोग करने पर विचार करें; इसमें निफ्टी putIfAbsent(K,V) विधि है जिसके साथ आप मूल रूप से तत्व सूची जोड़ सकते हैं यदि यह पहले से मौजूद नहीं है। इसके अलावा, मानचित्र मानों के लिए CopyOnWriteArrayList का उपयोग करने पर विचार करें यदि आपके उपयोग पैटर्न वारंट ऐसा कर रहे हैं।

13

आपके कोड में एक सूक्ष्म बग के लिए संभावित है।

[अद्यतन: चूंकि वह map.remove() का उपयोग कर रहा है, यह विवरण पूरी तरह मान्य नहीं है। मैं उस तथ्य को पहली बार याद किया। :(उनका कहना है कि बाहर doWork में। मैं बाकी जा रहा हूँ के रूप में है, लेकिन नेतृत्व बयान कहने के लिए वहाँ संभावित एक बग है बदल दिया है।]

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

आप मानचित्र में तरह

List<String> valuesList = new ArrayList<String>(); 
valuesList.add(value); 
synchronizedMap.put(key, Collections.synchronizedList(valuesList)); // sync'd list 

एक तुल्यकालन सूची डालने से इसे ठीक कर सकता है वैकल्पिक रूप से आप मानचित्र पर सिंक्रनाइज़ सकता है आप doWork (में सूची तक पहुँच है, जबकि):

public void doWork(String key) { 
    List<String> values = null; 
    while ((values = synchronizedMap.remove(key)) != null) { 
     synchronized (synchronizedMap) { 
      //do something with values 
     } 
    } 
    } 

अंतिम विकल्प कुछ हद तक समेकन को सीमित करेगा, लेकिन कुछ हद तक स्पष्ट आईएमओ है।

इसके अलावा, ConcurrentHashMap के बारे में एक त्वरित नोट। यह वास्तव में उपयोगी वर्ग है, लेकिन हमेशा सिंक्रनाइज़ हैश मैप्स के लिए उपयुक्त प्रतिस्थापन नहीं है। इसके Javadocs से हवाला देते हुए,

इस वर्ग पूरी तरह से प्रोग्राम हैं जो अपने धागा सुरक्षा पर लेकिन इसकी तुल्यकालन विवरण पर नहीं भरोसा में Hashtable के साथ अंतर-संचालित है।

दूसरे शब्दों में, putIfAbsent() परमाणु आवेषण के लिए बहुत अच्छा है लेकिन यह गारंटी नहीं देता कि मानचित्र के अन्य हिस्सों उस कॉल के दौरान नहीं बदले जाएंगे; यह केवल परमाणु की गारंटी देता है। अपने नमूना कार्यक्रम में, आप डाल() एस के अलावा अन्य चीज़ों के लिए (सिंक्रनाइज़) हैश मैप के सिंक्रनाइज़ेशन विवरण पर भरोसा कर रहे हैं।

अंतिम बात। :) से इस महान उद्धरण प्रैक्टिस में जावा कंसुरेंसी हमेशा मुझे डिबगिंग बहु-थ्रेडेड प्रोग्राम डिज़ाइन करने में मदद करता है।

प्रत्येक उत्परिवर्तनीय राज्य चर के लिए जो एक से अधिक धागे तक पहुंचा जा सकता है, उस चर के सभी उपयोग उसी लॉक के साथ किया जाना चाहिए।

+0

मैं बग के बारे में अपनी बात को देखने के अगर मैं synchronizedMap.get साथ सूची तक पहुंचते थे()। जब से मैं) को हटाने (उपयोग कर रहा हूँ, नहीं होना चाहिए अगले एक नया ArrayList बनाने कि कुंजी के साथ जोड़ सकते हैं और एक ही है कि मैं doWork में उपयोग कर रहा हूँ के साथ हस्तक्षेप नहीं? –

+0

सही! मैं पूरी तरह से अपने हटाने से पहले breezed। – JLR

+1

प्रत्येक म्यूटेबल स्टेट वैरिएबल के लिए जिसे एक से अधिक धागे से एक्सेस किया जा सकता है, उस चर के सभी एक्सेसों को उसी लॉक के साथ किया जाना चाहिए। ---- मैं आम तौर पर एक निजी संपत्ति जोड़ता हूं जो सिर्फ एक नया ऑब्जेक्ट() है और मेरे synconization ब्लॉक के लिए इसका उपयोग करें। इस तरह मैं उस संदर्भ के लिए कच्चे के माध्यम से सब कुछ जानता हूँ। तुल्यकालित (objectInVar) {} – AnthonyJClink

8

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

synchronized (synchronizedMap) { 
    if (synchronizedMap.containsKey(key)) { 
     synchronizedMap.get(key).add(value); 
    } 
    else { 
     List<String> valuesList = new ArrayList<String>(); 
     valuesList.add(value); 
     synchronizedMap.put(key, valuesList); 
    } 
} 

इस कोड

synchronizedMap.get(key).add(value); 

और

synchronizedMap.put(key, valuesList); 

विधि कॉल पिछले

synchronizedMap.containsKey(key) 
का परिणाम पर भरोसा कर रहे हैं: के लिए इस कोड पर एक नज़र डालते हैं

मिले होड कॉल

यदि विधि कॉल का अनुक्रम सिंक्रनाइज़ नहीं किया गया तो परिणाम गलत हो सकता है। उदाहरण thread 1 के लिए विधि निष्पादित हो रहा है addToMap() और thread 2 विधि निष्पादित हो रहा है doWork() विधि के अनुक्रम synchronizedMap वस्तु पर कॉल इस प्रकार हो सकती है: Thread 1 विधि

synchronizedMap.containsKey(key) 

मार डाला गया है और परिणाम "true है "। कि ऑपरेटिंग सिस्टम के बाद thread 2 के निष्पादन नियंत्रण बंद है और यह

synchronizedMap.remove(key) 

मार डाला गया है उसके बाद निष्पादन नियंत्रण thread 1 वापस करने के लिए बदल दिया गया है और यह उदाहरण

synchronizedMap.get(key).add(value); 

synchronizedMap वस्तु विश्वास के लिए मार डाला गया है key और NullPointerException फेंक दिया जाएगा क्योंकि synchronizedMap.get(key) null वापस आ जाएगा। यदि synchronizedMap ऑब्जेक्ट पर विधि कॉल का अनुक्रम एक दूसरे के परिणामों पर निर्भर नहीं है तो आपको अनुक्रम को सिंक्रनाइज़ करने की आवश्यकता नहीं है। उदाहरण के लिए आप इस क्रम सिंक्रनाइज़ करने की आवश्यकता नहीं है:

synchronizedMap.put(key1, valuesList1); 
synchronizedMap.put(key2, valuesList2); 

यहाँ

synchronizedMap.put(key2, valuesList2); 

विधि कॉल पिछले

synchronizedMap.put(key1, valuesList1); 

विधि कॉल के परिणामों पर निर्भर नहीं करता है (यह परवाह नहीं है कि कुछ थ्रेड ने दो विधि कॉल के बीच में हस्तक्षेप किया है और उदाहरण के लिए key1 हटा दिया गया है)।

1

जिस तरह से आपने सिंक्रनाइज़ किया है वह सही है। लेकिन एक पकड़

  1. संग्रह ढांचे द्वारा प्रदान किए गए सिंक्रनाइज़ किए गए रैपर यह सुनिश्चित करता है कि विधि कॉल Ie जोड़ें/प्राप्त/युक्त पारस्परिक रूप से अनन्य चलाएगा।

हालांकि वास्तविक दुनिया में आप आमतौर पर मूल्य डालने से पहले मानचित्र से पूछताछ करेंगे। इसलिए आपको दो संचालन करने की आवश्यकता होगी और इसलिए एक सिंक्रनाइज़ ब्लॉक की आवश्यकता है। तो जिस तरह से आपने इसका इस्तेमाल किया है वह सही है। तथापि।

  1. आप संग्रह ढांचे में उपलब्ध मानचित्र के एक समवर्ती कार्यान्वयन का उपयोग कर सकते थे। 'ConcurrentHashMap' लाभ

ए। इसमें एक एपीआई 'putIfAbsent' है जो एक ही सामान करेगा लेकिन एक अधिक कुशल तरीके से।

बी। इसके कुशल: डी। CocurrentMap बस ताले चाबियाँ इसलिए यह पूरे मानचित्र की दुनिया को अवरुद्ध नहीं कर रहा है। जहां आपने कुंजी और साथ ही मूल्यों को अवरुद्ध कर दिया है।

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

संबंधित मुद्दे