2011-03-31 70 views
33

我找到了AtomicIntegerAtomicLong,但是其中是AtomicFloat(或AtomicDouble)?也許有一些竅門?Java:有沒有AtomicFloat或AtomicDouble?

+1

沒有一個。你的用例是什麼? – 2011-03-31 19:50:15

+2

在Java 8中添加[DoubleAdder](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/DoubleAdder.html)可能符合您的需求。 – kuporific 2014-09-17 21:50:39

回答

54

java.util.concurrent package狀態下面的API文檔:

[...]另外,僅針對那些在預定的應用中很常用的類型提供的類。例如,沒有用於表示字節的原子類。在那些您不希望這樣做的罕見情況下,您可以使用AtomicInteger來保存字節值,並進行適當的轉換。 您也可以使用Float.floatToIntBitsFloat.intBitstoFloat轉換來保存花車,並使用Double.doubleToLongBitsDouble.longBitsToDouble轉換進行雙擊。

我不是說這是一個方便的解決方案,但是這似乎是解釋。我想你可能會想換一個AtomicInteger,並提供訪問方法getFloat/setFloat


事實上,我身邊一個寫。在這裏你去:

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

也可以在Guava中使用AtomicDouble http://docs.guava-libraries.googlecode.com/git-history/v11.0.2/javadoc/com/google/common/util/concurrent/AtomicDouble.html – codeplay 2013-10-31 09:41:33

+0

這是缺少一個這個功能很有用:'addAndGet'(或者'getAndAdd';並不重要)。 Guava'AtomicDouble'和Java 8'DoubleAdder'都有。所有這些關於用例的問題:自然地累積來自不同線程的殘差總和! – 2016-01-14 17:11:20

+0

@JimPivarski,'addAndGet'可以以'getAndSet'實現的相同方式實現。只需通過支持AtomicInteger的位。 – aioobe 2016-01-15 11:55:25

6

你也許可以使用AtomicReference<Float>來代替。我認爲AtomicIntegerAtomicLong可以獲得特殊的課程,因爲它們對計算很有用。

+11

'AtomicReference.compareAndSet'通過身份而不是等號進行比較,因此它不能替代假設的'AtomicFloat'。 – 2011-04-05 23:58:38

0

你確定你需要它嗎?

原子類主要被設計爲用於實現非阻塞數據結構和相關基礎結構類的構建塊。 compareAndSet方法不是鎖定的一般替代方法。它僅適用於對象的關鍵更新僅限於單個變量的情況。

Here是對原子變量設計要解決的問題的解釋。

+4

*你確定你需要它嗎?* - 也許他只是好奇:-)我認爲這是一個完全合法的問題。 – aioobe 2011-03-31 19:55:27

+2

@aioobe是的,但我認爲最好是瞭解爲什麼AtomicInteger存在,而不是提供可能不是真正需要的解決方案。 – 2011-03-31 20:00:02

1

這將是可怕的低效率實施(但它是可能的)。本質上來說它根據原子數據類型說話毫無意義,因爲對數據類型的操作是原子操作,而不是數據類型本身(也許你知道它,但只是想澄清這一點)。所有這些東西都會混在一起。在OS中你經常需要它們來管理鎖和信號燈,這就是爲什麼許多處理器都有原子整數指令。對於浮點數通常不會實現,因此它們通過將浮點操作包裝在受信號量保護的塊中(通過原子整數實現)來實現。

在高級別的java中,它自己鎖定浮動並沒有問題(你是對的,他們可能已經實現了它),但爲了提高效率,你必須使用低級別的asm來實現它們,所以它非常實用爲高級java用戶提供了一些利用低級別彙編指令的功能。

事實上,我看到很少有應用程序在原子浮動操作很有用。我偶然發現了它們,但是非常罕見,並且始終有可能重新解決浮點部分沒有發生併發問題。

5

我也很驚訝沒有內置的解決方案。 用例是獲取由併發線程集合發出的值的浮點和,而不使用內存使用量與值的數量。例如,併發線程是預測引擎,您希望在一個位置監視來自所有預測引擎的預測 - 否 - 真值殘差的總和。同時嘗試添加到一個樸素的計數器會導致計數丟失(與整數計數器完全相同)。

一個ConcurrentLinkedQueue可以收集這些值來概括,但除非有一個專門爲減少排隊的線程(持續運行result += q.poll()直到poll返回null,然後q.add(result)並稍等片刻它再次填滿)的大小隊列將增長到總和值的數量。

Java 8擁有DoubleAdder和Guava擁有AtomicDouble(請參閱其他問題的評論),但這不會幫助圖書館開發人員以最小的依賴關係爲目標定位舊的Java。我看了一個DoubleAdder codeAtomicDouble code的樣本,我發現我感到驚訝:他們只是重試加入,然後是compareAndSet,直到這樣做不是錯誤的。嘗試寫入的線程數量在爭用時可能會增加,但除非它們處於完美的鎖定步驟中,否則有些將贏得比賽並阻止其他人繼續重試。

這裏是一個Scala實現他們做了什麼:

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 
    } 
} 

和嘗試的Java翻譯:

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

同步是非常昂貴的。在暫停和喚醒線程所需的時間內,您可以在while循環中運行代碼幾千次。 – TomWolk 2017-06-30 13:03:56

0

雖然這裏的一些答案一些實現沒有提供一個完整和完整的一個

這一個是。它是AtomicDouble而不是AtomicFloat,因爲它比float更精確。

由於一些張貼在這裏實現,包括他們缺乏更新功能的谷歌番石榴,所以如操作:

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); 
     } 

}