itu*_*tun 43 java concurrency atomic
我找到了AtomicInteger,AtomicLong但是AtomicFloat(或AtomicDouble)在哪里?也许有一些伎俩?
aio*_*obe 70
该java.util.concurrent软件包的API文档声明如下:
[...]此外,仅为那些在预期应用程序中常用的类型提供类.例如,没有用于表示字节的原子类.在您不希望这样做的那些罕见情况下,您可以使用a
AtomicInteger来保存字节值,并进行适当的转换.您还可以使用Float.floatToIntBits和Float.intBitstoFloat转换使用浮点数,并使用Double.doubleToLongBits和Double.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)
你也许可以用一个AtomicReference<Float>代替.我认为AtomicInteger并AtomicLong获得特殊课程,因为它们对计数有用.
我也很惊讶没有内置的解决方案.用例是获取并发线程集合发出的值的浮点总和,而不使用内存,使用值的数量进行缩放.例如,并发线程是预测引擎,您希望在一个位置监视来自所有预测引擎的预测 - 减去真值残差的总和.同时尝试添加到初始计数器将导致计数丢失(与整数计数器完全相同).
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(在你知道你被允许之前不要计算总和)更新它).那么为什么呢?