2017-05-28 11 views
8
ConcurrentHashMap#computeIfAbsent से

जावाडोक कहतेपरिणाम

गणना छोटे और सरल होना चाहिए, और अद्यतन करने के लिए इस नक्शे के किसी भी अन्य मैपिंग प्रयास नहीं करना चाहिए।

लेकिन, mappingFunction अंदर मैं क्या देखते हैं, remove() और clear() तरीकों का उपयोग कर से ठीक काम करता है। उदाहरण के लिए इस

Key element = elements.computeIfAbsent(key, e -> { 
    if (usages.size() == maxSize) { 
     elements.remove(oldest); 
    } 
    return loader.load(key); 
}); 

क्या बुरा हटाने का उपयोग कर के परिणामों() mappingFunction अंदर विधि हो सकता है?

+0

इस तरह की एक कल्पना बहुत सावधानी से पढ़ी जानी चाहिए। "प्रयास नहीं करना चाहिए" शब्द इंगित करते हैं कि यदि आप नियम का पालन नहीं करते हैं तो शायद आप कहीं और तोड़ सकते हैं। लेकिन एंडी की तुलना में मैं हमेशा वास्तव में दिलचस्पी लेता हूं कि ऐसे प्रतिबंध क्यों लगाए जाते हैं। –

उत्तर

4

जावाडोक स्पष्ट रूप से कारण बताते हैं:

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

आप ताला लगा के रूप में यह HashTable जैसे पुराने धागा सुरक्षित मानचित्र कक्षाओं के लिए मामला है किया जा रहा बिना भूल जाते हैं कि ConcurrentHashMap एक धागा सुरक्षित मानचित्र का उपयोग करने का एक तरीका प्रदान बनाया गया है नहीं किया है।
जब मानचित्र पर कोई संशोधन होता है, तो यह केवल संबंधित मैपिंग को ताला लगाता है, न कि संपूर्ण मानचित्र।

ConcurrentHashMap अद्यतन के लिए retrievals की पूर्ण संगामिति और उच्च उम्मीद संगामिति समर्थन एक हैश तालिका है।

computeIfAbsent() एक नई पद्धति जावा में जोड़ा है 8.
तो बुरी तरह से इस्तेमाल किया है, वह यह है कि, computeIfAbsent() के शरीर कि पहले से ही कुंजी विधि को पास किए जाने की मैपिंग ताले में, आप एक और लॉक कुंजी है, तो आप उस पथ में प्रवेश करें जहां आप ConcurrentHashMap के उद्देश्य को हरा सकते हैं, अंततः आप दो मैपिंग लॉक कर देंगे।
अगर आप computeIfAbsent() के अंदर और मैपिंग लॉक करते हैं तो समस्या की कल्पना करें और यह विधि बिल्कुल कम नहीं है। मानचित्र पर Concurrency पहुंच धीमा हो जाएगा।

तो computeIfAbsent() का जावाडोक ConcurrentHashMap के सिद्धांतों को याद करके इस संभावित मुद्दे पर जोर देता है: इसे सरल और तेज़ रखें।


यहां समस्या का वर्णन करने वाला उदाहरण कोड है।
मान लें कि हमारे पास ConcurrentHashMap<Integer, String> का उदाहरण है।

हम दो धागे कि इसका इस्तेमाल शुरू कर देंगे:

  • पहले धागा: thread1 कि कुंजी 1
  • दूसरे धागे से computeIfAbsent() का आह्वान: thread2 कि कुंजी 2
साथ computeIfAbsent() का आह्वान

thread1 एक तेज़ पर्याप्त कार्य निष्पादित करता है लेकिन यह सलाह का पालन नहीं करता है computeIfAbsent() javadoc: यह computeIfAbsent() में कुंजी 2 अपडेट करता है, यह एक और मैपिंग है जिसका उपयोग विधि के वर्तमान संदर्भ में किया जाता है (जो कि 1 है)।

thread2 एक लंबे समय तक पर्याप्त कार्य निष्पादित करता है। यह javadoc की सलाह के बाद 2 कुंजी computeIfAbsent() के साथ आमंत्रित करता है: यह इसके कार्यान्वयन में किसी भी अन्य मानचित्रण को अद्यतन नहीं करता है।
लंबे कार्य को अनुकरण करने के लिए, हम पैरामीटर के रूप में 5000 के साथ Thread.sleep() विधि का उपयोग कर सकते हैं।

इस विशिष्ट स्थिति के लिए, यदि thread1 से पहले thread2 शुरू होता है, thread1 में map.put(2, someValue); के आह्वान करते हुए thread2 अवरुद्ध हो जाएगा computeIfAbsent() कि कुंजी 2 की मैपिंग ताले की न हो।

अंत में, हम एक ConcurrentHashMap उदाहरण है कि ब्लॉक प्रमुख 2 5 दौरान सेकंड के मानचित्रण, जबकि computeIfAbsent() कुंजी 1 के मानचित्रण के साथ शुरू हो जाती है मिलता है।
यह भ्रामक, प्रभावी नहीं है और यह ConcurrentHashMap इरादा और computeIfAbsent() वर्णन जो इरादा मौजूदा कुंजी के लिए मूल्य की गणना कर रहा है के खिलाफ जाता है:

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

नमूना कोड:

import java.util.concurrent.ConcurrentHashMap; 

public class BlockingCallOfComputeIfAbsentWithConcurrentHashMap { 

    public static void main(String[] args) throws InterruptedException { 
    ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>(); 

    Thread thread1 = new Thread() { 
     @Override 
     public void run() { 
      map.computeIfAbsent(1, e -> { 
       String valueForKey2 = map.get(2); 
       System.out.println("thread1 : get() returns with value for key 2 = " + valueForKey2); 
       String oldValueForKey2 = map.put(2, "newValue"); 
       System.out.println("thread1 : after put() returns, previous value for key 2 = " + oldValueForKey2); 
       return map.get(2); 
      }); 
     } 
    }; 

    Thread thread2 = new Thread() { 
     @Override 
     public void run() { 
      map.computeIfAbsent(2, e -> { 
      try { 
       Thread.sleep(5000); 
      } catch (Exception e1) { 
       e1.printStackTrace(); 
      } 
      String value = "valueSetByThread2"; 
      System.out.println("thread2 : computeIfAbsent() returns with value for key 2 = " + value); 
      return value; 
      }); 
     } 
    }; 

    thread2.start(); 
    Thread.sleep(1000); 
    thread1.start(); 
    } 
} 

उत्पादन के रूप में हम हमेशा मिलती है:

thread1: (मिल) के लिए कुंजी 2 = अशक्त

thread2 मूल्य के साथ प्रस्तुत करती है: computeIfAbsent() के लिए कुंजी 2 = मूल्य के साथ रिटर्न valueSetByThread2

थ्रेड 1: डालने के बाद() रिटर्न, कुंजी 2 = वैल्यूसेटबेट थ्रेड 2

यह writt है एन तेजी से ConcurrentHashMap पर पढ़ता अवरुद्ध नहीं कर रहे:

thread1: (मिल) के लिए कुंजी 2 = अशक्त

मूल्य के साथ देता है, लेकिन यह:

thread1: पुट के बाद () रिटर्न, कुंजी 2 = वैल्यूसेटबेट थ्रेड 2

केवल आउटपुट केवल तभी होता है जब थ्रेड 2 लौटाया जाता है computeIfAbsent() की।

5

एक खराब परिणाम का एक उदाहरण है:

ConcurrentHashMap<Integer,String> cmap = new ConcurrentHashMap<>(); 
cmap.computeIfAbsent (1, e-> {cmap.remove (1); return "x";}); 

इस कोड को एक गतिरोध का कारण बनता है।

2

ऐसी सलाह सलाह की तरह है कि सड़क के बीच में न चलें। आप इसे कर सकते हैं, और आप कार द्वारा मारा नहीं जा सकता है; यदि आप कार आने वाले हैं तो आप रास्ते से बाहर निकलने में भी सक्षम हो सकते हैं।

हालांकि, अगर आप पहले स्थान पर फुटपाथ (फुटपाथ) पर रहे तो आप सुरक्षित रहेंगे।

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

हालांकि, एपीआई के कार्यान्वयन एपीआई दस्तावेज द्वारा वर्णित अनुबंध की बाधाओं के भीतर कार्यान्वयन को बदलने के लिए स्वतंत्र हैं। वे एक बदलाव कर सकते हैं जो कल आपके काम को बंद कर देता है, क्योंकि वे व्यवहार को संरक्षित करने के लिए कोई दायित्व नहीं रखते हैं जिसे वे स्पष्ट रूप से उपयोग करने के खिलाफ चेतावनी देते हैं।

तो, आपके प्रश्नों का उत्तर देने के लिए कि क्या बुरा परिणाम हो सकता है: सचमुच कुछ भी (ठीक है, जो कुछ भी सामान्य रूप से पूरा होता है या RuntimeException फेंकता है); और आप समय के साथ या विभिन्न JVMs पर एक ही परिणाम का जरूरी नहीं देखेंगे।

फुटपाथ पर बने रहें: ऐसा न करें जो दस्तावेज आपको नहीं बताता है।

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