Java:没有AtomicFloat或AtomicDouble吗?

itu*_*tun 43 java concurrency atomic

我找到了AtomicInteger,AtomicLong但是AtomicFloat(或AtomicDouble)在哪里?也许有一些伎俩?

aio*_*obe 70

java.util.concurrent软件包的API文档声明如下:

[...]此外,仅为那些在预期应用程序中常用的类型提供类.例如,没有用于表示字节的原子类.在您不希望这样做的那些罕见情况下,您可以使用a 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();          }

}
Run Code Online (Sandbox Code Playgroud)

  • 也可以在Guava中使用AtomicDouble http://docs.guava-libraries.googlecode.com/git-history/v11.0.2/javadoc/com/google/common/util/concurrent/AtomicDouble.html (12认同)
  • 这缺少一个有用的功能:`addAndGet`(或`getAndAdd`;不管哪个)。Guava `AtomicDouble` 和 Java 8 `DoubleAdder` 都有。关于用例的所有这些问题:自然地累积来自不同线程的残差总和! (6认同)

Col*_*inD 7

你也许可以用一个AtomicReference<Float>代替.我认为AtomicIntegerAtomicLong获得特殊课程,因为它们对计数有用.

  • `AtomicReference.compareAndSet`按身份而不是相等进行比较,所以它不能替代假设的'AtomicFloat`. (13认同)

Jim*_*ski 6

我也很惊讶没有内置的解决方案.用例是获取并发线程集合发出的值的浮点总和,而不使用内存,使用值的数量进行缩放.例如,并发线程是预测引擎,您希望在一个位置监视来自所有预测引擎的预测 - 减去真值残差的总和.同时尝试添加到初始计数器将导致计数丢失(与整数计数器完全相同).

A ConcurrentLinkedQueue可以将值收集到sum,但除非有专门用于减少该队列的线程(result += q.poll()在轮询返回之前一直运行null,然后q.add(result)等待片刻再次填满),队列的大小将增加到值的数量总结.

Java 8 DoubleAdder和Guava一样AtomicDouble(请参阅其他问题的评论),但这并不能帮助图书馆开发人员以最小的依赖关系定位旧Java.我查看了DoubleAdder代码AtomicDouble代码的示例,我发现让我感到惊讶的是:他们只是重试添加,然后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
    }
}
Run Code Online (Sandbox Code Playgroud)

和尝试的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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它工作(Scala版本测试了数百个线程),并提供了一种概括的方法Double.

但是,我没有看到任何理由为什么在写入时比同步更快或更优先.阻塞解决方案也会让一些线程等待,而其他线程会增加计数器,但保证所有线程最终都会完成(不依赖于不完美的时序)并且不会浪费CPU(在你知道你被允许之前不要计算总和)更新它).那么为什么呢?