2011-09-27 11 views
10

मैं जावा में एक गोलाकार काउंटर लागू करना चाहता हूं। प्रत्येक अनुरोध पर काउंटर बढ़ाना चाहिए (परमाणु रूप से) और ऊपरी सीमा तक पहुंचने पर 0जावा में एक समवर्ती परिपत्र टिकर (काउंटर) को कैसे कार्यान्वित करें?

इसे लागू करने का सबसे अच्छा तरीका क्या होगा और क्या कोई मौजूदा कार्यान्वयन होगा?

उत्तर

6

आपको लगता है कि या तो कैस या synchronized का उपयोग कर विवाद के बारे में चिंतित है तो आप प्रस्तावित JSR 166e LongAdder (source, javadoc) की तरह और अधिक परिष्कृत कुछ विचार कर सकते हैं कर रहे हैं।

यह मल्टीथ्रेडेड एक्सेस पर कम विवाद के साथ एक सीधा काउंटर है। आप इसे बेनकाब करने के लिए लपेट सकते हैं (वर्तमान मान मोड अधिकतम मूल्य)। यही है, लिपटे मूल्य को बिल्कुल स्टोर न करें।

1

आप java.util.concurrent.atomic.AtomicInteger कक्षा का उपयोग परमाणु रूप से वृद्धि के लिए कर सकते हैं। ऊपरी बाउंड और रोलिंग को 0 पर वापस सेट करने के लिए, आपको इसे बाहरी रूप से करने की आवश्यकता होगी ... शायद यह सब अपने स्वयं के रैपर वर्ग के भीतर encapsulating।

दरअसल, ऐसा लगता है कि आप ऊपरी बाउंड की जांच के लिए compareAndSet का उपयोग कर सकते हैं, और उसके बाद 0 पर रोल कर सकते हैं।

18

यह AtomicInteger के ऊपर इस तरह के एक काउंटर को लागू करना आसान है:

public class CyclicCounter { 

    private final int maxVal; 
    private final AtomicInteger ai = new AtomicInteger(0); 

    public CyclicCounter(int maxVal) { 
     this.maxVal = maxVal; 
    } 

    public int cyclicallyIncrementAndGet() { 
     int curVal, newVal; 
     do { 
      curVal = this.ai.get(); 
      newVal = (curVal + 1) % this.maxVal; 
     } while (!this.ai.compareAndSet(curVal, newVal)); 
     return newVal; 
    } 

} 
+0

मुझे आश्चर्य है अगर यह वास्तव में परमाणु हो सकता है अगर धागा के बीच प्राथमिकता खो देता है '' 'this.ai.get()' '' और '' '} जबकि (! This.ai.compareAndSet (curVal, newal)); '' ' –

+0

@ रेनाटो वापस: हाँ, यह होगा। सीएएस अर्थपूर्ण गारंटी देता है कि। –

2

मैं व्यक्तिगत रूप से लगता है के रूप में यह एक रेस शर्त है जो अपने अद्यतन प्रयास "असफल" कर सकता है और इसका मतलब परिचय AtomicInteger समाधान एक छोटे से बदसूरत है दोहराए जाने के लिए (थोड़ी देर के भीतर पुनरावृत्ति करके) अद्यतन समय को एक महत्वपूर्ण खंड में पूरे ऑपरेशन करने से कम निर्धारिती बनाते हैं।

अपना खुद का काउंटर लिखना इतना छोटा है कि मैं उस दृष्टिकोण की अनुशंसा करता हूं। यह एक ओओ-परिप्रेक्ष्य से भी अच्छा है क्योंकि यह केवल उन परिचालनों को उजागर करता है जिन्हें आप करने की अनुमति है।

public class Counter { 
    private final int max; 
    private int count; 

    public Counter(int max) { 
    if (max < 1) { throw new IllegalArgumentException(); } 

    this.max = max; 
    } 

    public synchronized int getCount() { 
    return count; 
    } 

    public synchronized int increment() { 
    count = (count + 1) % max; 
    return count; 
    } 
} 

संपादित

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

+0

यदि आपको लगता है कि आप कभी काउंटर प्रिंट करना चाहते हैं तो आपको ToString() को भी लागू करना होगा। – GreenMatt

+0

निष्पादन के अनुसार परमाणु इंटीजर तेजी से प्रतीत होता है। Https://gist.github.com/1245597 – sheki

+0

एडमस्की देखें, सभी समय लूप करता है जो हम आम तौर पर सिंक्रनाइज़ेशन से प्राप्त लाभ प्रदान करते हैं। आपके समाधान में एक ही गैर-निर्धारिक अद्यतन समय होगा (कॉलर के दृष्टिकोण से)। – CPerkins

2

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

public class Count { 
    private final AtomicLong counter = new AtomicLong(); 
    private static final long MAX_VALUE = 500; 
    public long getCount() { 
     return counter.get() % MAX_VALUE; 
    } 
    public long incrementAndGet(){ 
     return counter.incrementAndGet() % MAX_VALUE; 

    } 
} 

आपको Long.MAX_VALUE केस भी हल करना होगा।

5
जावा के साथ
public class CyclicCounter { 

    private final int maxVal; 
    private final AtomicInteger counter = new AtomicInteger(0); 

    public CyclicCounter(int maxVal) { 
     this.maxVal = maxVal; 
    } 

    return counter.accumulateAndGet(1, (index, inc) -> { 
     return ++index >= maxVal ? 0 : index; 
    });  

}

1

मैं, एक कस्टम अक्का रूटिंग तर्क जो डिफ़ॉल्ट की तुलना में अलग नेटवर्क भूमि के ऊपर से बचने के लिए होना था के लिए एक समान परिपत्र टिकर बनाने के लिए मेरे तर्क के बाद से अगले राउटर को चुनने के लिए है।

नोट: सुझाव जावा 8 कार्यान्वयन से कॉपी किया गया:

import akka.routing.Routee; 
import akka.routing.RoutingLogic; 
import scala.collection.immutable.IndexedSeq; 

import java.util.concurrent.atomic.AtomicInteger; 

public class CircularRoutingLogic implements RoutingLogic { 

    final AtomicInteger cycler = new AtomicInteger(); 

    @Override 
    public Routee select(Object message, IndexedSeq<Routee> routees) { 
    final int size = routees.size(); 
    return size == 0 ? null : routees.apply(cycler.getAndUpdate(index -> ++index < size ? index : 0)); 
    } 
} 
0

अत्यधिक गहन परिपत्र समानांतर में एक से अधिक थ्रेड से वृद्धि की जाती काउंटर के लिए, मैं LongAdder का उपयोग कर की सिफारिश करेंगे (जावा 8 के बाद से, मूल विचार को देखने के Striped64.java के अंदर), क्योंकि यह AtomicLong की तुलना में अधिक स्केलेबल है। इसे उपर्युक्त समाधानों में अनुकूलित करना आसान है।

यह माना जाता है कि get ऑपरेशन LongAdder में इतना बार नहीं होता है। counter.get पर कॉल करते समय, इसे 'counter.get% max_number' पर लागू करें। हां, मॉड्यूलो ऑपरेशन महंगा है, लेकिन यह इस उपयोग-मामले के लिए कम है, जो कुल प्रदर्शन लागत को कम करना चाहिए।

हालांकि याद रखें, get ऑपरेशन गैर-अवरुद्ध है, न ही परमाणु है।

+1

क्या LongAdder को विधि मिलती है? क्या आपका मतलब योग() है? योग(), हां, गैर-परमाणु है, तो क्या यह ऊपरी सीमा से 0 तक रोलिंग के साथ कोई समस्या नहीं पैदा करेगा? वैसे भी, यह * वास्तविक * कार्यान्वयन देखना दिलचस्प है ... –