2011-03-31 21 views
33

मुझे AtomicInteger, AtomicLong मिला है, लेकिन AtomicFloat (या AtomicDouble) कहां है? शायद कुछ चाल है?जावा: क्या कोई परमाणु फ्लोट या परमाणु डबल नहीं है?

+1

वहाँ एक नहीं है यह एक तुम क्या करने की अनुमति देता है। आपका उपयोग केस क्या है? –

+2

जावा 8 में जोड़ा गया, [डबलएडर] (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/DoubleAdder.html) आपकी आवश्यकताओं के अनुरूप हो सकता है। – kuporific

उत्तर

54

निम्नलिखित java.util.concurrent package राज्यों के लिए एपीआई डॉक्स:

[...] इसके अतिरिक्त, कक्षाएं केवल उन है जो आम तौर इरादा अनुप्रयोगों में उपयोगी होते हैं के लिए प्रदान की जाती हैं। उदाहरण के लिए, बाइट का प्रतिनिधित्व करने के लिए कोई परमाणु वर्ग नहीं है। उन कम मामलों में जहां आप ऐसा करना चाहते हैं, आप बाइट मानों को पकड़ने के लिए AtomicInteger का उपयोग कर सकते हैं, और उचित रूप से डाले जा सकते हैं। आप Float.floatToIntBits और Float.intBitstoFloat रूपांतरणों का उपयोग करके फ्लोट्स को भी पकड़ सकते हैं, और Double.doubleToLongBits और Double.longBitsToDouble रूपांतरणों का उपयोग करके युगल भी कर सकते हैं।

मैं नहीं दावा कर रहा हूँ कि यह एक सुविधाजनक समाधान है, लेकिन वह स्पष्टीकरण हो रहा है। मुझे लगता है कि आप शायद


मैं वास्तव में हो गया चारों ओर एक लेखन getFloat/setFloat आदि के लिए एक AtomicInteger लपेट और उपयोग के तरीकों प्रदान करने के लिए चाहते हैं। यहां आप जाएं:

import java.util.concurrent.atomic.AtomicInteger; 
import static java.lang.Float.*; 

class AtomicFloat extends Number { 

    private AtomicInteger bits; 

    public AtomicFloat() { 
     this(0f); 
    } 

    public AtomicFloat(float initialValue) { 
     bits = new AtomicInteger(floatToIntBits(initialValue)); 
    } 

    public final boolean compareAndSet(float expect, float update) { 
     return bits.compareAndSet(floatToIntBits(expect), 
            floatToIntBits(update)); 
    } 

    public final void set(float newValue) { 
     bits.set(floatToIntBits(newValue)); 
    } 

    public final float get() { 
     return intBitsToFloat(bits.get()); 
    } 

    public float floatValue() { 
     return get(); 
    } 

    public final float getAndSet(float newValue) { 
     return intBitsToFloat(bits.getAndSet(floatToIntBits(newValue))); 
    } 

    public final boolean weakCompareAndSet(float expect, float update) { 
     return bits.weakCompareAndSet(floatToIntBits(expect), 
             floatToIntBits(update)); 
    } 

    public double doubleValue() { return (double) floatValue(); } 
    public int intValue()  { return (int) get();   } 
    public long longValue()  { return (long) get();   } 

} 
+10

गुवा में परमाणु डबल का उपयोग भी कर सकता है http://docs.guava-libraries.googlecode.com/git-history/v11.0.2/javadoc/com/google/common/util/concurrent/AtomicDouble.html – codeplay

+0

यह एक गायब है सुविधा उपयोगी होगी: 'addAndGet' (या' getAndAdd'; इससे कोई फर्क नहीं पड़ता)। अमरूद 'परमाणु डबल' और जावा 8 'डबलएडर' में यह है। उपयोग-मामले के बारे में ये सभी प्रश्न: विभिन्न धागे से आने वाले शेष अवशेषों को जमा करने के लिए, स्वाभाविक रूप से! –

+0

@JimPivarski, 'addAndGet' को उसी तरह लागू किया जा सकता है जैसे' getAndSet' लागू किया गया है। बस बैकिंग परमाणु इंटेगर के बिट्स के माध्यम से जाओ। – aioobe

6

आप शायद AtomicReference<Float> का उपयोग कर सकते हैं। मुझे लगता है कि AtomicInteger और AtomicLong विशेष कक्षाएं प्राप्त करें क्योंकि वे गिनने के लिए उपयोगी हैं।

+11

'AtomicReference.compareAndSet' पहचान द्वारा तुलना करता है और समानता से नहीं, इसलिए यह hypothetical' AtomicFloat' के लिए कोई प्रतिस्थापन नहीं है। –

0

क्या आपको यकीन है कि आपको इसकी आवश्यकता है?

परमाणु कक्षाओं मुख्य रूप से गैर-अवरुद्ध डेटा संरचनाओं और संबंधित बुनियादी ढांचे वर्गों को लागू करने के लिए ब्लॉक के निर्माण के रूप में तैयार कर रहे हैं। तुलना औरसेट विधि लॉकिंग के लिए एक सामान्य प्रतिस्थापन नहीं है। यह तब लागू होता है जब किसी ऑब्जेक्ट के लिए महत्वपूर्ण अपडेट एक चर से सीमित होते हैं।

Here उन समस्याओं की व्याख्या है जो परमाणु चर को हल करने के लिए डिज़ाइन किए गए थे।

+4

* क्या आपको यकीन है कि आपको इसकी आवश्यकता है? * - शायद वह सिर्फ उत्सुक है :-) मुझे लगता है कि यह पूछने के लिए एक बिल्कुल वैध सवाल है। – aioobe

+2

@aioobe हाँ, लेकिन मुझे लगता है कि यह पढ़ने के लिए बेहतर है कि क्यों 'एटमिकइंटर' एक ऐसा समाधान प्रदान करने से मौजूद है जो शायद वास्तव में आवश्यक नहीं है। –

1

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

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

असल में मैंने बहुत ही कम आवेदन देखा जहां परमाणु फ्लोट ऑपरेशन उपयोगी हैं। मैं उनके पास आया, लेकिन बहुत दुर्लभ था और इस समस्या को सुधारना हमेशा संभव था कि फ्लोट भाग पर समेकन नहीं हुआ था।

5

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

एक ConcurrentLinkedQueue के आकार योग करने के लिए मूल्यों को एकत्र कर सकते हैं, लेकिन जब तक कि वहाँ एक धागा है कि कतार को कम करने के लिए समर्पित है (लगातार result += q.poll() जब तक चुनाव null देता है, तो q.add(result) चल रहा है और एक क्षण प्रतीक्षा इसे फिर से भरने के लिए), कतार मूल्य के मूल्यों की संख्या में वृद्धि होगी।

जावा 8 DoubleAdder है और अमरूद AtomicDouble है (अन्य प्रश्न पर टिप्पणी देखें), लेकिन यह है कि पुस्तकालय डेवलपर्स कम से कम निर्भरता के साथ पुराने जावा को निशाना बनाने में मदद नहीं करता। मैंने DoubleAdder code और AtomicDouble code का एक नमूना देखा, और जो मुझे मिला वह मुझे आश्चर्यचकित करता है: जब तक ऐसा करने से गलत नहीं होता है, तब तक वे compareAndSet के बाद फिर से प्रयास करते हैं। लिखने का प्रयास करने वाले धागे की संख्या में विवाद होने पर बढ़ोतरी हो सकती है, लेकिन जब तक कि वे सही लॉक-चरण में न हों, कुछ लोग दौड़ जीतेंगे और रास्ते से बाहर निकलेंगे जबकि अन्य लोग पुनः प्रयास करते रहेंगे।

class AtomicDouble { 
    private val value = new AtomicReference(java.lang.Double.valueOf(0.0)) 
    @tailrec 
    final def getAndAdd(delta: Double): Double = { 
     val currentValue = value.get 
     val newValue = java.lang.Double.valueOf(currentValue.doubleValue + delta) 
     if (value.compareAndSet(currentValue, newValue)) 
      currentValue.doubleValue 
     else 
      getAndAdd(delta) // try, try again 
    } 
} 

और किसी प्रयास किए गए जावा अनुवाद:

यहाँ वे क्या करते हैं की एक स्काला कार्यान्वयन है

class AtomicDouble { 
    private AtomicReference<Double> value = new AtomicReference(Double.valueOf(0.0)); 
    double getAndAdd(double delta) { 
     while (true) { 
      Double currentValue = value.get(); 
      Double newValue = Double.valueOf(currentValue.doubleValue() + delta); 
      if (value.compareAndSet(currentValue, newValue)) 
       return currentValue.doubleValue(); 
     } 
    } 
} 

यह काम करता है (स्काला संस्करण धागे के सैकड़ों के साथ परीक्षण), और करने के लिए एक तरीका प्रदान करता है Double से सामान्यीकृत करें।

हालांकि, मुझे कोई कारण नहीं दिख रहा है कि यह केवल लिखने पर सिंक्रनाइज़ करने पर तेज़ या पसंदीदा क्यों होगा। एक अवरुद्ध समाधान कुछ धागे भी इंतजार करेगा जबकि अन्य काउंटर में वृद्धि करेंगे, लेकिन गारंटी के साथ कि सभी अंततः खत्म हो जाएंगे (अपूर्ण समय पर कोई निर्भरता नहीं) और कोई बर्बाद CPU नहीं है (जब तक आपको पता न हो कि आपको अनुमति है इसे अपडेट करो)। तो यह क्यों?

+0

सिंक्रनाइज़ेशन बहुत महंगा है। उस समय एक धागे को निलंबित करने और जागने के लिए जरूरी समय में आप कुछ हज़ार बार लूप के दौरान कोड चला सकते थे। – TomWolk

0

यह जावा मुद्दा नहीं है, सभी भाषाएं इससे ग्रस्त हैं। सभी http://x86.renejeschke.de/html/file_module_x86_id_41.html

इन पूर्णांकों पर काम करते हैं और एफपीयू की pipelined प्रकृति यह बहुत कठिन तैरता/डबल्स के लिए लागू करने के लिए बनाता है:

विधानसभा निर्देश जो परमाणु तुलना और स्वैप संचालन करने के लिए नीचे संकलन के वेरिएंट हैं।

0

हालांकि कुछ उत्तर यहाँ कुछ कार्यान्वयन से कोई भी एक पूर्ण की पेशकश और एक पूरा करने के लिए लग रहे हैं।

यह करता है। यह परमाणु डबल है और परमाणु फ्लोट नहीं है क्योंकि इसमें फ्लोट की तुलना में अधिक सटीकता है।

कार्यान्वयन यहां पोस्ट, गूगल अमरूद वे अपडेटर कार्यों की कमी शामिल है, के कुछ है, तो इस तरह के रूप संचालन:

average.set(average.get() > x ? dosomething(y) : y) ; 

पूरी तरह से परमाणु नहीं किया जा सकता है।

average.updateAndGet(new DoubleUnaryOperator() {     
    @Override 
    public double applyAsDouble(double previous) { 
      return previous > x ? dosomething(y) : y; 
    } 
}); 

पूर्ण कार्यान्वयन एक ही तरीके से नीचे के रूप में AtomicLong में पाया:

import static java.lang.Double.doubleToLongBits; 
import static java.lang.Double.longBitsToDouble; 

import java.util.concurrent.atomic.AtomicLong; 
import java.util.function.DoubleBinaryOperator; 
import java.util.function.DoubleUnaryOperator; 

public final class AtomicDouble extends Number { 
     private static final long serialVersionUID = 12327722191124184L; 

     private final AtomicLong bits; 

     public AtomicDouble() { 
       this(0.0d); 
     } 

     public AtomicDouble(double initialValue) { 
       bits = new AtomicLong(toLong(initialValue)); 
     } 

     /** 
     * Atomically sets the value to the given updated value 
     * if the current value {@code ==} the expected value. 
     * 
     * @param expect the expected value 
     * @param update the new value 
     * @return {@code true} if successful. False return indicates that 
     * the actual value was not equal to the expected value. 
     */ 
     public final boolean compareAndSet(double expect, double update) { 
       return bits.compareAndSet(toLong(expect), toLong(update)); 
     }  

     /** 
     * Sets to the given value. 
     * 
     * @param newValue the new value 
     */ 
     public final void set(double newValue) { 
       bits.set(toLong(newValue)); 
     } 

     public final double get() { 
       return toDouble(bits.get()); 
     } 

     /** 
     * Atomically sets to the given value and returns the old value. 
     * 
     * @param newValue the new value 
     * @return the previous value 
     */ 
     public final double getAndSet(double newValue) { 
       return toDouble(bits.getAndSet(toLong(newValue))); 
     } 

     /** 
     * Atomically sets the value to the given updated value 
     * if the current value {@code ==} the expected value. 
     * 
     * <p><a href="package-summary.html#weakCompareAndSet">May fail 
     * spuriously and does not provide ordering guarantees</a>, so is 
     * only rarely an appropriate alternative to {@code compareAndSet}. 
     * 
     * @param expect the expected value 
     * @param update the new value 
     * @return {@code true} if successful 
     */ 
     public final boolean weakCompareAndSet(double expect, double update) { 
       return bits.weakCompareAndSet(toLong(expect), toLong(update)); 
     } 

     /** 
     * Atomically updates the current value with the results of 
     * applying the given function to the current and given values, 
     * returning the updated value. The function should be 
     * side-effect-free, since it may be re-applied when attempted 
     * updates fail due to contention among threads. The function 
     * is applied with the current value as its first argument, 
     * and the given update as the second argument. 
     * 
     * @param x     the update value 
     * @param accumulatorFunction a side-effect-free function of two arguments 
     * @return the updated value 
     * @since 1.8 
     */ 
     public final double accumulateAndGet(double x, DoubleBinaryOperator accumulatorFunction) { 
       double prev, next; 
       do { 
         prev = get(); 
         next = accumulatorFunction.applyAsDouble(prev, x); 
       } while (!compareAndSet(prev, next)); 
       return next; 
     } 

     /** 
     * Atomically adds the given value to the current value. 
     * 
     * @param delta the value to add 
     * @return the updated value 
     */ 
     public final double addAndGet(double delta) { 
       return toDouble(bits.addAndGet(toLong(delta))); 
     } 

     /** 
     * Atomically decrements by one the current value. 
     * 
     * @return the updated value 
     */ 
     public final double decrementAndGet() { 
       return addAndGet(-1.0d); 
     } 

     /** 
     * Atomically updates the current value with the results of 
     * applying the given function to the current and given values, 
     * returning the previous value. The function should be 
     * side-effect-free, since it may be re-applied when attempted 
     * updates fail due to contention among threads. The function 
     * is applied with the current value as its first argument, 
     * and the given update as the second argument. 
     * 
     * @param x     the update value 
     * @param accumulatorFunction a side-effect-free function of two arguments 
     * @return the previous value 
     * @since 1.8 
     */ 
     public final double getAndAccumulate(double x, DoubleBinaryOperator accumulatorFunction) { 
       double prev, next; 
       do { 
         prev = get(); 
         next = accumulatorFunction.applyAsDouble(prev, x); 
       } while (!compareAndSet(prev, next)); 
       return prev; 
     } 

     /** 
     * Atomically adds the given value to the current value. 
     * 
     * @param delta the value to add 
     * @return the previous value 
     */ 
     public final double getAndAdd(double delta) { 
       return toDouble(bits.getAndAdd(toLong(delta))); 
     } 

     public final double getAndDecrement() { 
       return getAndAdd(-1.0d); 
     } 

     /** 
     * Atomically increments by one the current value. 
     * 
     * @return the previous value 
     */ 
     public final double getAndIncrement() { 
       return getAndAdd(1.0d); 
     } 

     /** 
     * Atomically increments by one the current value. 
     * 
     * @return the updated value 
     */ 
     public final double incrementAndGet() { 
       return addAndGet(1.0d); 
     } 

     /** 
     * Atomically updates the current value with the results of 
     * applying the given function, returning the previous value. The 
     * function should be side-effect-free, since it may be re-applied 
     * when attempted updates fail due to contention among threads. 
     * 
     * @param updateFunction a side-effect-free function 
     * @return the previous value 
     * @since 1.8 
     */ 
     public final double getAndUpdate(DoubleUnaryOperator updateFunction) { 
       double prev, next; 
       do { 
         prev = get(); 
         next = updateFunction.applyAsDouble(prev); 
       } while (!compareAndSet(prev, next)); 
       return prev; 
     } 


     /** 
     * Eventually sets to the given value. 
     * 
     * @param newValue the new value 
     * @since 1.6 
     */ 
     public final void lazySet(double newValue) { 
       bits.lazySet(toLong(newValue)); 
       // unsafe.putOrderedLong(this, valueOffset, newValue); 
     } 

     /** 
     * Returns the value of this {@code AtomicLong} as a {@code long}. 
     */ 
     public long longValue() { 
       return (long) get(); 
     } 

     /** 
     * Returns the String representation of the current value. 
     * 
     * @return the String representation of the current value 
     */ 
     public String toString() { 
       return Double.toString(get()); 
     } 

     /** 
     * Atomically updates the current value with the results of 
     * applying the given function, returning the updated value. The 
     * function should be side-effect-free, since it may be re-applied 
     * when attempted updates fail due to contention among threads. 
     * 
     * @param updateFunction a side-effect-free function 
     * @return the updated value 
     * @since 1.8 
     */ 
     public final double updateAndGet(DoubleUnaryOperator updateFunction) { 
       double prev, next; 
       do { 
         prev = get(); 
         next = updateFunction.applyAsDouble(prev); 
       } while (!compareAndSet(prev, next)); 
       return next; 
     } 
     /** 
     * Returns the value of this {@code AtomicLong} as an {@code int} 
     * after a narrowing primitive conversion. 
     * 
     * @jls 5.1.3 Narrowing Primitive Conversions 
     */ 
     public int intValue() { 
       return (int) get(); 
     } 

     /** 
     * Returns the value of this {@code AtomicLong} as a {@code float} 
     * after a widening primitive conversion. 
     * 
     * @jls 5.1.2 Widening Primitive Conversions 
     */ 
     public float floatValue() { 
       return (float) get(); 
     } 

     /** 
     * Returns the value of this {@code AtomicLong} as a {@code double} 
     * after a widening primitive conversion. 
     * 
     * @jls 5.1.2 Widening Primitive Conversions 
     */ 
     public double doubleValue() { 
       return get(); 
     } 

     private static double toDouble(long l) { 
       return longBitsToDouble(l); 
     } 

     private static long toLong(double delta) { 
       return doubleToLongBits(delta); 
     } 

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