2017-05-04 14 views
15

मैं रिकर्सिवली फिबोनैकी संख्या की गणना, एक ConcurrentHashMap और computeIfAbsent() विधि के साथ के लिए एक कार्यक्रम लिखा था 10 to 20 से कार्यक्रम कभी नहीं रुकतीConcurrentHashMap और फाइबोनैचि संख्या असंगत परिणाम

public class Test { 
    static Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<>(); 

    public static void main(String[] args) { 
     System.out.println("Fibonacci result for 20 is" + fibonacci(20)); 
    } 

    static int fibonacci(int i) { 
     if (i == 0) 
      return i; 

     if (i == 1) 
      return 1; 

     return concurrentMap.computeIfAbsent(i, (key) -> { 
      System.out.println("Value is " + key); 
      return fibonacci(i - 2) + fibonacci(i - 1); 
     }); 
    } 
} 

कुछ एक मुझे बता सकते हैं क्यों यह हमेशा के लिए अटक जा रहा है?

+0

आपके पास नीचे स्पष्टीकरण है, लेकिन मैंने रिकर्सिव फिबोनाकी के बारे में जो कहा वह मान्य है; यदि आपको वास्तव में उच्च अनुक्रम Fibonacci संख्या उत्पन्न करने की आवश्यकता है तो गतिशील प्रोग्रामिंग का उपयोग करें। –

+0

@ टिमबीजलेसेन- हाँ मैं करूँगा .. मैं बस समवर्ती हैश मानचित्र के साथ खेल रहा था और पाया ... :) –

+1

@ टिमबीजलेसेन ओपी गतिशील प्रोग्रामिंग कर रहा था, केवल एक स्पष्ट तरीके से। फाइबोनैकी संख्या की प्रत्येक अवधि केवल गणना की जाती है अगर इसे पहले गणना नहीं की जाती है। यदि इसे पहले से गणना की गई थी, तो मान 'समवर्ती मैप' –

उत्तर

27

आप एक डेडलॉक मार रहे हैं।

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

मैं यहां ConcurrentHashMap का उपयोग करने के आपके निर्णय पर पुनर्विचार करूंगा।

+0

मुझे लगता है कि यह सही जवाब है। मैंने 'हैश मैप' के साथ एक ही कोड की कोशिश की और यह 20 से बड़े मानों के लिए काम करता है। दूसरी ओर, 'ConcurrentHashMap' के लिए कोड deadlocks जब i> = 14। – Eran

+0

@ ईरान मैं आपसे सहमत हूं। मैंने अपने उत्तर को इस आधार पर ध्वस्त कर दिया कि मुझे पता है कि रिकर्सिव फिबोनाकी अपेक्षाकृत छोटी संख्याओं में विफल रहता है, इस पर विचार किए बिना कि 'ConcurrentHashMap' का उपयोग +1 –

+4

का उपयोग किया जा रहा था [यह ConcurrentHashMap.computeIfAbsent से [JavaDocs] से लिंक करने योग्य हो सकता है (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#computeIfAbsent-K-java.util.function.Function-): "कुछ ने इस धागे पर अन्य धागे द्वारा अपडेट ऑपरेशन का प्रयास किया गणना के दौरान अवरुद्ध हो सकता है, इसलिए गणना कम और सरल होनी चाहिए, और इस मानचित्र के किसी भी अन्य मैपिंग को अपडेट करने का प्रयास नहीं करना चाहिए। " – Hulk

3

फिबोनासी संख्याओं की गणना के लिए यह रिकर्सन विधि घातीय जटिलता है। कैशिंग के साथ आप इसे रैखिक में वापस कर देते हैं, या आप रैखिक एल्गोरिदम प्राप्त करने के लिए रिकर्सन के बजाय सरल चक्र का उपयोग कर सकते हैं।

मुझे आश्चर्य है कि आप कैशिंग के लिए ConcurentHashMap का उपयोग क्यों कर रहे हैं। मैं या तो सरल नक्शा, या कैशिंग के लिए सरणी का उपयोग करेंगे।

मानचित्रों को सरणी के खिलाफ लाभ होता है, जब मानों को छिड़क दिया जाता है, लेकिन जब आपके पास संख्याओं का अनुक्रम होता है, तो आप सरल सरणी का उपयोग कर सकते हैं।

3

मैंने थ्रेड डंप लिया, और हम लॉक 0x000000076b70bba0 के साथ थ्रेड को मृत लॉक समस्या का कारण बन सकते हैं।

अगर गलत है तो कृपया मुझे सही करें। जबकि गणना कार्य प्रगति पर है

main - priority:5 - threadId:0x00000000021af000 - nativeId:0x2798 - state:RUNNABLE 
    stackTrace: 
    java.lang.Thread.State: RUNNABLE 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1674) 
    - locked <0x000000076b70bba0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.lambda$0(Test.java:20) 
    at Test$$Lambda$1/834600351.apply(Unknown Source) 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 
    - locked <0x000000076b70c720> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.lambda$0(Test.java:20) 
    at Test$$Lambda$1/834600351.apply(Unknown Source) 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 
    - locked <0x000000076b70c5c0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.lambda$0(Test.java:20) 
    at Test$$Lambda$1/834600351.apply(Unknown Source) 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 
    - locked <0x000000076b70c460> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.lambda$0(Test.java:20) 
    at Test$$Lambda$1/834600351.apply(Unknown Source) 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 
    - locked <0x000000076b70c300> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.lambda$0(Test.java:20) 
    at Test$$Lambda$1/834600351.apply(Unknown Source) 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 
    - locked <0x000000076b70c1a0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.lambda$0(Test.java:20) 
    at Test$$Lambda$1/834600351.apply(Unknown Source) 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 
    - locked <0x000000076b70c040> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.lambda$0(Test.java:20) 
    at Test$$Lambda$1/834600351.apply(Unknown Source) 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 
    - locked <0x000000076b70bee0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.lambda$0(Test.java:20) 
    at Test$$Lambda$1/834600351.apply(Unknown Source) 
    at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 
    - locked <0x000000076b70bba0> (a java.util.concurrent.ConcurrentHashMap$ReservationNode) 
    at Test.fibonacci(Test.java:18) 
    at Test.main(Test.java:8) 
    Locked ownable synchronizers: 
    - None 
+0

वास्तव में - और ConcurrentHashMap.java (मेरे jdk1.8.0_121 में) की 1674 लाइन सिंक्रनाइज़ (एफ) {'है, जहां' f' एक 'नोड' है ', इस मामले में एक' आरक्षण नोड '- शायद हेश के अनुसार ढेर के बहुत नीचे लॉक किया गया है। – Hulk

+2

... ये सभी निश्चित रूप से कार्यान्वयन विवरण हैं, और किसी भी समय बदल सकते हैं। महत्वपूर्ण बात ConcurrentHashMap.computeIfAbsent का अनुबंध है जो बताती है कि मैपिंग फ़ंक्शन "इस मानचित्र के किसी भी अन्य मैपिंग को अपडेट करने का प्रयास नहीं करना चाहिए"। (मेरी अन्य टिप्पणी भी देखें https://stackoverflow.com/questions/43774947/concurrenthashmap-and-fibonacci-number-inconsistent-result#comment74592799_43775058) – Hulk

1

अन्य धागे से इस नक्शे पर Oracle Doc

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

के रूप में ठीक ही जो सी सर्वोच्च जवाब में से कहा, ConcurrentHashMap के डिफ़ॉल्ट प्रारंभ एक SMA है तात्कालिकता के समय आवंटित बाल्टी की संख्या होगी।

ConcurrentHashMap का उपयोग करने का उद्देश्य नक्शा के समेकित संशोधन को कई धागे से अवरुद्ध करने की आवश्यकता के बिना (link) की अनुमति देना है।


आप अभी भी अपने आवेदन के लिए ConcurrentHashMap का उपयोग कर के साथ रहना चाहते हैं, तो मैं इसके निर्माण के दौरान initialCapacity बढ़ाने की सलाह देते हैं।

static Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<>(11); 

, तक फिबोनैकी श्रृंखला और 25


दस्तावेज़ के अनुसार सहित

ConcurrentHashMap() की गणना कर सकते
डिफ़ॉल्ट प्रारंभिक तालिका आकार के साथ एक नया, रिक्त मानचित्र बनाती (16)।

हालांकि, मैं इस पर भिन्न होना चाहता हूं क्योंकि मैंने देखा कि डिफ़ॉल्ट आकार बहुत छोटा है।
इसका कारण यह है कि जब आप ConcurrentHashMap<>(11) से ConcurrentHashMap<>() < - डिफ़ॉल्ट 16 होना चाहिए .. लेकिन यह नहीं है।

+0

यहां 2 गलतफहमी हैं: 1. 'ConcurrentHashMap' की डिफ़ॉल्ट प्रारंभिक क्षमता जावा डॉक्स में 16 जैसा बताया गया है। हालांकि, डिफ़ॉल्ट 'loadFactor' 0.75 है, इसलिए जैसे ही आप 13 वें तत्व को अपने मानचित्र पर डालते हैं, यह बढ़ने का प्रयास करता है। – Hulk

+0

2।सिर्फ इसलिए कि आपके मानचित्र में 16 की क्षमता है, इसका मतलब यह नहीं है कि इसमें 16 प्रविष्टियां डालने से उन्हें अलग बाल्टी में डाल दिया जाएगा – Hulk

+0

बस एक उदाहरण: कुंजी के साथ 'हैश मैप' भरें 'k = [0,1,2,4 , 9,16,25..100] '(मूल्य कोई फर्क नहीं पड़ता), और आप एक डीबगर की मदद से सत्यापित कर सकते हैं कि 16 डिफ़ॉल्ट बाल्टी में से केवल 4 में मान हैं, सूचकांक पर: 0, 1, 4 , 9। इंडेक्स 0 में बाल्टी में 0,16 और 64 की चाबियों के लिए मैपिंग शामिल है, इंडेक्स 1 में से एक को 1, 4 9 और 81 के लिए मैपिंग्स मिलते हैं। – Hulk

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