2009-09-08 17 views
38

मुझे एक डेटा संरचना की आवश्यकता है जो एक LinkedHashMap है और थ्रेड सुरक्षित है।क्या जावा में "LinkedConcurrentHashMap" डेटा संरचना है?

मैं यह कैसे कर सकता हूं?

+6

मुझे लगता है कि सवाल underspecified है। आप क्यों कहते हैं कि इसे "एक लिंक्ड हैश मैप" होना चाहिए? वास्तव में आप किस व्यवहार के बाद हैं? –

+0

इसी प्रकार का सवाल: http://stackoverflow.com/questions/1815646/how-to-implement-concurrenthashmap-with-features-similar-in-linkedhashmap – Vadzim

उत्तर

4

Collections.synchronizedMap(new LinkedHashMap())

+2

लेकिन यदि आप कोई समग्र करना चाहते हैं तो आपको अभी भी मैन्युअल रूप से सिंक्रनाइज़ करने की आवश्यकता होगी ConcurrentHashMap –

24

आप एक तुल्यकालन hashmap कि प्रविष्टि व्यवस्था को बनाए रखता पाने के लिए एक Collections.synchronizedMap में नक्शा लपेट कर सकते हैं। यह एक ConcurrentHashMap के रूप में उतना कुशल नहीं है (और ConcurrentMap के अतिरिक्त इंटरफ़ेस विधियों को लागू नहीं करता है) लेकिन यह आपको (कुछ हद तक) थ्रेड सुरक्षित व्यवहार प्राप्त करता है।

यहां तक ​​कि शक्तिशाली Google संग्रह भी इस विशेष समस्या को हल नहीं कर पाए हैं। हालांकि, one project है जो समस्या से निपटने का प्रयास करता है।

मैं सिंक्रनाइज़ेशन पर कुछ हद तक कहता हूं, क्योंकि पुनरावृत्ति अभी भी इस धागे में सुरक्षित नहीं है कि समवर्ती संशोधन अपवाद हो सकते हैं।

+1

+1 द्वारा प्रदान किए गए अतिरिक्त लोगों की तरह संचालन। मैं बस इसे जोड़ना चाहता हूं क्योंकि मुझे याद है, कारण यह नहीं है क्योंकि एक्सेस ऑर्डर के साथ व्यवहार को संरक्षित करने के लिए इसे पूरी तालिका को लॉक करने में मजबूर होना होगा (या जटिल होना चाहिए एक तरीका है जो या तो डेडलॉक प्रवण या धीमा होने का जोखिम उठाएगा) इसलिए एक साधारण लिंक्ड हैशैप के आसपास सिंक्रनाइज़ेशन करना ज्यादातर मामलों में समान रूप से समान रूप से कुशल होगा। स्पष्टीकरण एक whitewash हो सकता है लेकिन यह मुझे समझ में आता है। – Fredrik

+1

(अस्वीकरण: मैंने वोट नहीं दिया और न ही डाउनवोट)। इस दृष्टिकोण को पसंद नहीं है। सिंक्रनाइज़! = समवर्ती।यदि आपके पास मानचित्र पर गहन समानांतर नौकरी है, तो आपके एन -1 थ्रेड हर समय अवरुद्ध हो जाएंगे। जबकि समवर्ती अधिकांश स्थितियों में अवरुद्ध होने से बचना चाहिए। – juanmf

3

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

जब तक कुछ अपाचे लाइब्रेरी नहीं है जो आपने जो भी लागू किया है, उसे लागू करने के लिए आपको शायद एक लिंकड हैशैप का उपयोग करना होगा और अपने स्वयं के उपयुक्त सिंक्रनाइज़ {} ब्लॉक प्रदान करना होगा।

+0

हम्म, छः वर्षों के बाद डाउनवोट, और कोई कारण नहीं दिया गया। ऐसा इसलिए नहीं है क्योंकि सवाल बदल गया है कि मेरा जवाब अब गलत सवाल का जवाब दे रहा है। विचार? –

0

उत्तर बहुत अधिक नहीं है, सॉर्ट किया गया एक ConcurrentHashMap के बराबर कुछ भी नहीं है (जैसे LinkedHashMap)। जैसा कि अन्य लोगों ने इंगित किया है, आप Collections.synchronizedMap (-yourmap-) का उपयोग करके अपने संग्रह को लपेट सकते हैं, हालांकि यह आपको ठीक दाग लॉकिंग का एक ही स्तर नहीं देगा। यह बस हर ऑपरेशन पर पूरे मानचित्र को अवरुद्ध कर देगा।

आपकी सबसे अच्छी शर्त है कि मानचित्र के किसी भी पहुंच के आसपास सिंक्रनाइज़ किया जाए (जहां यह मायने रखता है, आपको गंदे पढ़ने की परवाह नहीं है) उदाहरण के लिए) या मानचित्र के चारों ओर एक रैपर लिखने के लिए यह निर्धारित करता है कि इसे कब करना चाहिए या ताला नहीं होना चाहिए।

+1

यदि आपने एक हैशैप को पढ़ा है, तो पढ़ना शायद गंदे नहीं हो सकता है, वे भ्रष्ट हो सकते हैं, अपवाद फेंक सकते हैं, विशेष रूप से बहु-सीपीयू वातावरण में। – Yishai

+2

समवर्ती अद्यतनों में हस्तक्षेप की वजह से हैश मैप एक अनंत लूप में जा रहा है। –

10

इस समस्या के कई अलग-अलग दृष्टिकोण हैं। आप इस्तेमाल कर सकते हैं:

Collections.synchronizedMap(new LinkedHashMap()); 
अन्य प्रतिक्रियाओं का सुझाव दिया है के रूप में

लेकिन यह कई gotchas आप के बारे में पता करने की आवश्यकता होगी है। सबसे विशेष रूप से यह है कि संग्रह पर पुनरावृत्ति करते समय आपको संग्रह सिंक्रनाइज़ लॉक को अक्सर पकड़ने की आवश्यकता होती है, जो बदले में अन्य धागे को तब तक संग्रह तक पहुंचने से रोकती है जब तक आप इसे फिर से पूरा नहीं कर लेते। (See Java theory and practice: Concurrent collections classes)। उदाहरण के लिए:

synchronized(map) { 
    for (Object obj: map) { 
     // Do work here 
    } 
} 

का उपयोग

new ConcurrentHashMap(); 

शायद एक बेहतर विकल्प के रूप में आप संग्रह इस पर पुनरावृति को लॉक करने के लिए की जरूरत नहीं होगी है।

अंत में, आप कार्यात्मक प्रोग्रामिंग दृष्टिकोण पर विचार करना चाहेंगे। यह है कि आप मानचित्र को अनिवार्य रूप से अपरिवर्तनीय मान सकते हैं। मौजूदा मानचित्र में जोड़ने के बजाय, आप एक नया निर्माण करेंगे जिसमें पुराने मानचित्र की सामग्री और नया जोड़ा शामिल है।यह पहली बार बहुत विचित्र लगता है, लेकिन वास्तव में यह तरीका है Scala deals with concurrency and collections

+0

स्कैला समस्या को कैसे हल करता है यह इंगित करने के लिए धन्यवाद। यह काफी आंख खोलने वाला है। –

+0

मुझे लगता है कि जावा भी CopyOnWriteArrayList में उस दृष्टिकोण का उपयोग करता है जहां यह प्रत्येक लेखन के बाद सूची की प्रतिलिपि बनाता है, प्रदर्शन व्यापक लगता है लेकिन इस तरह मुझे लगता है। –

7

one implementation Google कोड के तहत उपलब्ध है। उनकी साइट से उद्धरण:

सॉफ़्टवेयर कैश के रूप में उपयोग के लिए java.util.LinkedHashMap का एक उच्च प्रदर्शन संस्करण।

डिजाइन

  • एक समवर्ती लिंक्ड सूची बेदखली आदेश प्रदान करने के लिए एक ConcurrentHashMap के माध्यम से चलाता है।
  • प्रविष्टि का समर्थन करता है और आदेशित निष्कासन नीतियों (एफआईएफओ, एलआरयू, और दूसरा मौका) का उपयोग करता है।
+2

इस परियोजना के लिए मुख्य कम्युनेटर बेन मैनेज, एक Google सॉफ्टवेयर इंजीनियर है, इसलिए यह बहुत भरोसेमंद (आईएमएचओ) है। – Marco

4

आप एक ConcurrentSkipListMap, जावा SE/ईई 6 या इसके बाद में ही उपलब्ध उपयोग कर सकते हैं। यह उन चाबियों में ध्यान देने का क्रम है जो उनके प्राकृतिक क्रम के अनुसार क्रमबद्ध होते हैं। आपको एक तुलना करने की आवश्यकता है या कुंजी तुलनात्मक वस्तुओं की आवश्यकता है। एक लिंक किए गए हैश मैप व्यवहार की नकल करने के लिए (पुनरावृत्ति आदेश वह समय है जिसमें प्रविष्टियां शामिल की गई थीं) मैंने अपनी मुख्य वस्तुओं को हमेशा किसी अन्य ऑब्जेक्ट से अधिक होने की तुलना करने के लिए लागू किया जब तक कि यह बराबर न हो (जो भी आपके ऑब्जेक्ट के लिए है)। एक लपेटा हुआ सिंक्रनाइज़ किया गया हैश नक्शा पर्याप्त नहीं था क्योंकि http://www.ibm.com/developerworks/java/library/j-jtp07233.html में बताया गया है: "सिंक्रनाइज़ किए गए संग्रह रैपर, सिंक्रनाइज़मैप मैप और सिंक्रनाइज़लिस्ट, कभी-कभी सशर्त रूप से थ्रेड-सुरक्षित कहा जाता है - सभी व्यक्तिगत संचालन थ्रेड-सुरक्षित होते हैं, लेकिन संचालन के अनुक्रम जहां नियंत्रण प्रवाह पिछले परिचालनों के परिणामों पर निर्भर करता है, डेटा रेस के अधीन हो सकता है। लिस्टिंग 1 में पहला स्निपेट सामान्य पॉट-एफ़-अनुपस्थित मुहावरे दिखाता है - यदि कोई प्रविष्टि मानचित्र में पहले से मौजूद नहीं है, तो इसे जोड़ें। दुर्भाग्यवश, लिखा है, किसी अन्य धागे के लिए एक ही कुंजी के साथ एक मूल्य डालना संभव है जिसमें शामिल है केई() विधि रिटर्न और समय() विधि को कॉल करने के समय के बीच एक ही कुंजी के साथ एक मूल्य डालना संभव है। यदि आप निश्चित रूप से एक बार सम्मिलन सुनिश्चित करना चाहते हैं, तो आपको लपेटने की आवश्यकता है एक सिंक्रनाइज़ ब्लॉक के साथ बयानों की जोड़ी जो मानचित्र एम पर सिंक्रनाइज़ करती है। "

तो केवल एक ConcurrentSkipListMap है जो सामान्य ConcurrentHashMap से 3-5 गुना धीमा है, केवल मदद करता है।

+2

दुर्भाग्यवश सम्मिलन आदेश को परिभाषित करने के लिए कस्टम तुलनित्र काम नहीं करता है। यह मानता है कि तुलना करने के लिए पारित पहली वस्तु() नया डाला गया है। लेकिन ConcurrentSkipList मानचित्र भी पुनरावृत्त करते समय तुलना करता है (कम से कम 'map.entrySet() ') के साथ, इस मामले में आप नहीं जानते कि 2 ऑब्जेक्ट्स में से कौन सा पहला डाला गया है। जब तक इस ऑब्जेक्ट में कोई सृजन तिथि न हो - लेकिन यह उदाहरण के लिए स्ट्रिंग्स के सामान्य मामले के लिए असफल हो जाती है। –

1

मैंने अभी प्रविष्टि आदेश लिंक्ड कॉनकुरेंट हैशैप पर आधारित सिंक्रनाइज़ बाध्य एलआरयू मानचित्र की कोशिश की; सिंक्रनाइज़ेशन के लिए लॉक पढ़ें/लिखें। तो जब आप इटरेटर का उपयोग कर रहे हों; ConcurrentModificationException से बचने के लिए आपको WriteLock प्राप्त करना होगा।
इस की तुलना में बेहतर Collections.synchronizedMap.

public class LinkedConcurrentHashMap<K, V> { 

    private LinkedHashMap<K, V> linkedHashMap = null; 
    private final int cacheSize; 
    private ReadWriteLock readWriteLock = null; 

    public LinkedConcurrentHashMap(LinkedHashMap<K, V> psCacheMap, int size) { 
     this.linkedHashMap = psCacheMap; 
     cacheSize = size; 
     readWriteLock=new ReentrantReadWriteLock(); 
    } 

    public void put(K key, V value) throws SQLException{ 
     Lock writeLock=readWriteLock.writeLock(); 
     try{ 
      writeLock.lock(); 
      if(linkedHashMap.size() >= cacheSize && cacheSize > 0){ 
       K oldAgedKey = linkedHashMap.keySet().iterator().next(); 
       remove(oldAgedKey); 
      } 
      linkedHashMap.put(key, value); 
     }finally{ 
      writeLock.unlock(); 
     } 
    } 

    public V get(K key){ 
     Lock readLock=readWriteLock.readLock(); 
     try{ 
      readLock.lock(); 
      return linkedHashMap.get(key); 
     }finally{ 
      readLock.unlock(); 
     } 
    } 

    public boolean containsKey(K key){ 
     Lock readLock=readWriteLock.readLock(); 
     try{ 
      readLock.lock(); 
      return linkedHashMap.containsKey(key); 
     }finally{ 
      readLock.unlock(); 
     } 
    } 

    public V remove(K key){ 
     Lock writeLock=readWriteLock.writeLock(); 
     try{ 
      writeLock.lock(); 
      return linkedHashMap.remove(key); 
     }finally{ 
      writeLock.unlock(); 
     } 
    } 

    public ReadWriteLock getLock(){ 
     return readWriteLock; 
    } 

    public Set<Map.Entry<K, V>> entrySet(){ 
     return linkedHashMap.entrySet(); 
    } 
} 
+0

यदि आप 'ConcurrentMap ' लागू करते हैं, तो आपको मुफ्त में सिंक्रनाइज़ेशन मिल जाएगा ('ReadWriteLock' की आवश्यकता नहीं है)। – naXa

+0

@naXa मैंने बस एलआरयू मानचित्र की कोशिश की, इसलिए नक्शा भरा हुआ है; एक नए तत्व को जोड़ने के लिए हाल ही में इस्तेमाल किए गए तत्व को पट्टा हटा देना चाहिए, इसलिए मुझे लॉक कार्यान्वयन की आवश्यकता है। –

+0

और मुझे पता है कि एक्सेसऑर्डर बूलियन मान का उपयोग करके LRUMap को LinkedHashMap (int प्रारंभिक क्षमता, फ्लोट लोड फैक्टर, बूलियन एक्सेस ऑर्डर) के माध्यम से कार्यान्वित किया जा सकता है। लेकिन जो सिंक्रनाइज़ नहीं है। –

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