2010-07-26 14 views
28

में संग्रहीत परमाणु रूप से बढ़ते काउंटर वेब मैप में विभिन्न स्थानों से कुछ मीट्रिक एकत्र करना चाहते हैं। इसे सरल रखने के लिए, ये सभी काउंटर होंगे और इसलिए एकमात्र संशोधक ऑपरेशन उन्हें 1ConcurrentHashMap

वृद्धि को समवर्ती और अक्सर समझा जाएगा। पढ़ता है (आंकड़े डंपिंग) एक दुर्लभ ऑपरेशन है।

मैं ConcurrentHashMap का उपयोग करने के बारे में सोच रहा था। मुद्दा यह है कि काउंटर सही ढंग से वृद्धि कैसे करें। चूंकि मानचित्र में "वृद्धि" ऑपरेशन नहीं है, इसलिए मुझे पहले वर्तमान मान को पढ़ने की आवश्यकता है, मानचित्र में नया मान डालने से इसे बढ़ाएं। अधिक कोड के बिना, यह एक परमाणु ऑपरेशन नहीं है।

यह तुल्यकालन के बिना इस लक्ष्य को हासिल करने के लिए (जो ConcurrentHashMap का उद्देश्य विफल हो जाएगा) संभव है? क्या मुझे Guava देखने की आवश्यकता है?

किसी भी पॉइंटर्स के लिए धन्यवाद।


पीएस
वहाँ ऐसा (Most efficient way to increment a Map value in Java), लेकिन प्रदर्शन और नहीं बहु सूत्रण

अद्यतन
एक ही विषय पर खोजों के माध्यम से यहां पहुंचने के उन लोगों के लिए पर ध्यान केंद्रित पर एक संबंधित सवाल यह है: नीचे दिए गए उत्तर के अलावा, वहाँ एक उपयोगी है presentation जो आकस्मिक रूप से एक ही विषय को शामिल करता है। स्लाइड्स 24-33 देखें।

उत्तर

7

आप बहुत करीब हैं। आप ConcurrentHashMap<Key, AtomicLong> जैसी कुछ कोशिश क्यों नहीं करते? यदि आपका Key एस (मीट्रिक) अपरिवर्तनीय है, तो आप मानक HashMap का उपयोग भी कर सकते हैं (यदि वे केवल पढ़ने के लिए थ्रेडसेफ हैं, लेकिन आपको अच्छी तरह से Google संग्रह या Collections.unmodifiableMap इत्यादि से ImmutableMap के साथ यह स्पष्ट करने की सलाह दी जाएगी)।

इस तरह, आप आंकड़े टक्कर के लिए map.get(myKey).incrementAndGet() का उपयोग कर सकते हैं।

+1

बस एक में 'HashMap' स्टोर करने के लिए मत भूलना 'अंतिम' सदस्य और मानचित्र को 'unmodifiable' wrapper में बेहतर लपेटें। अभी तक बेहतर, आप अमरूद से 'इमटेबल मैप' का उपयोग कर सकते हैं (Google संग्रह के सुपरसेट) और यह वास्तव में वास्तव में तेज़ होना चाहिए। –

+0

@Zwei: अच्छा बिंदु, उस सलाह को शामिल करने के लिए उत्तर संपादित किया गया :) –

+0

मेट्रिक्स की सूची डेटा की आपूर्ति के रूप में बनाई गई है (यानी नक्शा की चाबियाँ सिस्टम रन के रूप में जोड़ दी जाएंगी और विभिन्न संग्रह बिंदु प्रभावित होंगे; सूची बनाना प्राथमिकता त्रुटि-प्रवण होगी)। मैं परमाणु लोंग की वृद्धि और भूल गया(), यह वही है जो मुझे चाहिए। यदि यह अस्तित्व में नहीं था, तो मैं सोच रहा था कि मेट्रिक्स कलेक्टरों के लिए काउंटर बढ़ाने के लिए एक और दृष्टिकोण नहीं था, लेकिन सिंगलटन द्वारा बनाए गए कतार में ऐसा करने के लिए अनुरोध जोड़ने के लिए। इस प्रकार, कॉलर्स केवल एक सूची में() जोड़ते हैं, जो समय-समय पर पढ़ा जाता है और संसाधित होता है। हालांकि, उतना आसान नहीं है। – wishihadabettername

4

अन्य की तुलना में AtomicLong के साथ जा रहा है, तो आप हमेशा की तरह cas लूप बात कर सकते हैं:

private final ConcurrentMap<Key,Long> counts = 
    new ConcurrentHashMap<Key,Long>(); 

public void increment(Key key) { 
    if (counts.putIfAbsent(key, 1)) == null) { 
     return; 
    } 

    Long old; 
    do { 
     old = counts.get(key); 
    } while (!counts.replace(key, old, old+1)); // Assumes no removal. 
} 

(मैं नहीं एक do लिखा है - उम्र के लिए while पाश।) छोटे मूल्यों के लिए

Long शायद "कैश्ड" होगा। लंबे मूल्यों के लिए, इसे आवंटन की आवश्यकता हो सकती है। लेकिन आवंटन वास्तव में बेहद तेज़ हैं (और आप आगे कैश कर सकते हैं) - सबसे खराब मामले में, आप जो अपेक्षा करते हैं उस पर निर्भर करता है।

+0

मुझे लगता है कि यह सही है। पिछले टूटे हुए कोड को देखने वाले किसी को भी क्षमा करें। (आप इसे पुनर्व्यवस्थित कर सकते हैं ताकि यह 'get' के साथ शुरू हो जाए, नल की तुलना करें और केवल तब' putIfAbsent' 'को सामान्य' जबकि 'लूप के साथ जारी रखें।) –

14

अमरूद की नई AtomicLongMap (रिहाई 11 में) इस जरूरत को संबोधित कर सकते हैं।

+0

यह एक सही उत्तर है! कस्टम कोड नमूने यहां सूचीबद्ध अमरूद के बीच समवर्ती गलती की संभावना की तुलना करें। – snowindy

0

ऐसा करने की आवश्यकता हो गई। मैं ConcurrentHashMap + AtomicInteger का उपयोग कर रहा हूं। इसके अलावा, ReentrantRW लॉक परमाणु फ्लश (बहुत समान व्यवहार) के लिए पेश किया गया था।

10 कुंजी और प्रत्येक कुंजी प्रति 10 धागे के साथ परीक्षण किया गया। कुछ भी नहीं खो गया था। मैं बस अभी तक कई निस्तब्धता धागे प्रयास नहीं किया है, लेकिन आशा है कि यह काम करेगा।

भारी सिंगलसर्मोड फ्लश मुझे यातना दे रहा है ... मैं आरडब्लॉक को हटाना चाहता हूं और छोटे टुकड़ों में फ्लश करना चाहता हूं। आने वाला कल।

private ConcurrentHashMap<String,AtomicInteger> counters = new ConcurrentHashMap<String, AtomicInteger>(); 
private ReadWriteLock rwLock = new ReentrantReadWriteLock(); 

public void count(String invoker) { 

    rwLock.readLock().lock(); 

    try{ 
     AtomicInteger currentValue = counters.get(invoker); 
     // if entry is absent - initialize it. If other thread has added value before - we will yield and not replace existing value 
     if(currentValue == null){ 
      // value we want to init with 
      AtomicInteger newValue = new AtomicInteger(0); 
      // try to put and get old 
      AtomicInteger oldValue = counters.putIfAbsent(invoker, newValue); 
      // if old value not null - our insertion failed, lets use old value as it's in the map 
      // if old value is null - our value was inserted - lets use it 
      currentValue = oldValue != null ? oldValue : newValue; 
     } 

     // counter +1 
     currentValue.incrementAndGet(); 
    }finally { 
     rwLock.readLock().unlock(); 
    } 

} 

/** 
* @return Map with counting results 
*/ 
public Map<String, Integer> getCount() { 
    // stop all updates (readlocks) 
    rwLock.writeLock().lock(); 
    try{ 
     HashMap<String, Integer> resultMap = new HashMap<String, Integer>(); 
     // read all Integers to a new map 
     for(Map.Entry<String,AtomicInteger> entry: counters.entrySet()){ 
      resultMap.put(entry.getKey(), entry.getValue().intValue()); 
     } 
     // reset ConcurrentMap 
     counters.clear(); 
     return resultMap; 

    }finally { 
     rwLock.writeLock().unlock(); 
    } 

} 
+0

हर बार GetCount को लिखने से रोकने के लिए कहा जाता है। यह मूल्य स्थिरता की गारंटी देगा। लेकिन प्रदर्शन को प्रभावित करेगा। विधि पर लिंक के साथ – Sudhakar

20

जावा 8 में:

ConcurrentHashMap<String, LongAdder> map = new ConcurrentHashMap<>(); 

map.computeIfAbsent("key", k -> new LongAdder()).increment(); 
+0

संक्षिप्त रूप में: 'LongAdder :: increment' – pacman

+0

@pacman क्या आप समझा सकते हैं? – ZhekaKozlov

+0

मेरा मतलब लिंक पर जावा 8 विधि था: 'AtomicInteger atomicInt = new AtomicInteger (0); atomicInt :: incrementAndGet' – pacman

-2

निम्नलिखित कोड गिनती शब्द 'आवृत्तियों के लिए तुल्यकालन के बिना मेरे लिए काम किया

protected void updateFreqCountForText(Map<String, Integer> wordFreq, String line) { 
      ConcurrentMap<String, Integer> wordFreqConc = (ConcurrentMap<String, Integer>) wordFreq; 
      Pattern pattern = Pattern.compile("[a-zA-Z]+"); 
      Matcher matcher = pattern.matcher(line); 
      while (matcher.find()) { 
       String word = matcher.group().toLowerCase(); 
       Integer oldCount; 

       oldCount = wordFreqConc.get(word); 
       if (oldCount == null) { 
        oldCount = wordFreqConc.putIfAbsent(word, 1); 
        if (oldCount == null) 
         continue; 
        else wordFreqConc.put(word, oldCount + 1); 
       } 
       else 
        do { 
         oldCount = wordFreqConc.get(word); 
        } while (!wordFreqConc.replace(word, oldCount, oldCount + 1)); 

      } 
     } 
+1

* में निम्न समाधान का सुझाव दिया गया है निम्न कोड के लिए काम किया गया सिंक्रनाइज़ेशन के बिना * थ्रेड सुरक्षा का एक पूरी तरह से अर्थहीन संकेतक है। – shmosel

+0

डाउनवॉटेड। यह कोड विफल हो सकता है यदि 3 त्वरित उत्तराधिकार में एक कुंजी जोड़ने का प्रयास करता है। – Persixty

+0

कहाँ विफल? अन्य में या शाखा? – user1264304

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