Pio*_*app 51 java multithreading
我找到了以下Java代码.
for (int type = 0; type < typeCount; type++)
synchronized(result) {
result[type] += parts[type];
}
}
Run Code Online (Sandbox Code Playgroud)
其中,result和parts是double[].
我知道原始类型的基本操作是线程安全的,但我不确定+=.如果上述synchronized是必要的,是否有更好的类来处理这种操作?
Ste*_*n C 66
+=不可以.该操作不是线程安全的.对于涉及将共享字段或数组元素赋值为线程安全的任何表达式,它需要锁定和/或适当的"事先发生"关系链.
(将字段声明为volatile,"之前发生"关系存在...但仅限于读取和写入操作.+=操作包括读取和写入.这些是单独原子的,但序列不是.赋值表达式使用=涉及一个或多个读取(在右侧)和写入.该序列也不是原子的.)
有关完整的故事,请阅读JLS 17.4 ...或Brian Goetz等人的"Java Concurrency in Action"的相关章节.
据我所知,基本类型的基本操作是线程安全的...
实际上,这是一个不正确的前提:
此double类型还有一个问题.JLS(17.7)说:
"出于Java编程语言内存模型的目的,对非易失性long或double值的单个写入被视为两个单独的写入:每个32位半写一个.这可能导致线程看到的情况一次写入的64位值的前32位,另一次写入的第二位32位."
"写入和读取volatile的long和double值总是原子的."
在评论中,你问:
那么我应该使用哪种类型来避免全局同步,这会阻止此循环中的所有线程?
在这种情况下(您正在更新a double[],除了与锁或原始互斥锁同步之外别无选择.
如果您有int[]或者long[]您可以用AtomicIntegerArray或替换它们AtomicLongArray并使用这些类的无锁更新.但是没有AtomicDoubleArray课,甚至没有AtomicDouble课.
(更新 - 有人指出Guava提供了一个AtomicDoubleArray类,所以这将是一个选项.实际上是一个好的.)
避免"全局锁定"和大规模争用问题的一种方法可能是将阵列划分为名义区域,每个区域都有自己的锁定.这样,一个线程只需要阻止另一个线程,如果它们使用相同的数组区域.(单个写入器/多个读取器锁也可以帮助...如果绝大多数访问都是读取.)
尽管事实上没有AtomicDouble或AtomicDoubleArray在java中,您可以轻松地创建自己的基于AtomicLongArray.
static class AtomicDoubleArray {
private final AtomicLongArray inner;
public AtomicDoubleArray(int length) {
inner = new AtomicLongArray(length);
}
public int length() {
return inner.length();
}
public double get(int i) {
return Double.longBitsToDouble(inner.get(i));
}
public void set(int i, double newValue) {
inner.set(i, Double.doubleToLongBits(newValue));
}
public void add(int i, double delta) {
long prevLong, nextLong;
do {
prevLong = inner.get(i);
nextLong = Double.doubleToLongBits(Double.longBitsToDouble(prevLong) + delta);
} while (!inner.compareAndSet(i, prevLong, nextLong));
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我使用Double.doubleToLongBits并Double.longBitsToDouble存储Doubles为Longs在AtomicLongArray.它们都具有相同的位大小,因此精度不会丢失(除了-NaN,但我认为它不重要).
在Java 8中,实现add可以更容易,因为您可以使用在Java 1.8中添加的accumulateAndGet方法AtomicLongArray.
更新:看来我实际上重新实现了番石榴的AtomicDoubleArray.
即使是正常的"双"数据类型在32位JVM中也不是线程安全的(因为它不是原子的),因为它在Java中需要8个字节(涉及2*32位操作).
| 归档时间: |
|
| 查看次数: |
4665 次 |
| 最近记录: |