Java中的+ =运算符是线程安全的吗?
我find了下面的Java代码。
for (int type = 0; type < typeCount; type++) synchronized(result) { result[type] += parts[type]; } }   result和parts是double[] 。 
 我知道原始types的基本操作是线程安全的,但我不知道+= 。 如果上面的synchronized是必要的,那么有没有更好的类来处理这种操作? 
 不可以。 +=操作不是线程安全的。 它需要locking和/或一个适当的“发生之前”关系链,涉及任何涉及到共享字段或数组元素的线程安全的expression式。 
  (有一个被声明为volatile的字段,“happen-before”关系存在……但仅限于读写操作, +=操作由读和写组成,它们分别是primefaces的,但是序列不是。大多数赋值expression式使用=包含一个或多个读取(在右边)和一个写入,这个序列也不是primefaces的。 
有关完整的故事,请阅读JLS 17.4或Brian Goetz等人撰写的“Java并发实践”相关章节。
据我所知基本types的基本操作是线程安全的…
其实这是一个不正确的前提:
- 考虑数组的情况
- 认为expression式通常由一系列操作组成,并且一系列的primefaces操作不能保证是primefaces操作。
  doubletypes还有一个额外的问题。  JLS( 17.7 )说: 
“对于Java编程语言内存模型来说,对非易失性long或double值的单次写入被视为两次分开的写入:每个32位一次写入,这可能导致线程看到来自一个写入的64位值的第一个32位,以及来自另一个写入的第二个32位。
“写入和读取volatile和long值总是primefaces的。”
在一个评论中,你问:
那么,我应该使用什么types来避免全局同步,这会停止循环内的所有线程?
 在这种情况下(你正在更新一个double[] ,除了用锁或原语互斥体进行同步外,别无select。 
 如果你有一个int[]或一个long[]你可以用AtomicIntegerArray或AtomicLongArrayreplace它们,并利用这些类的无锁更新。 但是没有AtomicDoubleArray类,甚至没有AtomicDouble类。 
  ( 更新 – 有人指出,番石榴提供了一个AtomicDoubleArray类,所以这将是一个选项,实际上是一个好的。 
避免“全局locking”和大规模争用问题的一种方法可能是将arrays划分为名义区域,每个区域都有自己的locking。 这样,一个线程只需要阻塞另一个线程,如果他们正在使用数组的相同区域。 (如果绝大多数访问是读取,那么单个写入器/多个读取器锁也可以帮助太多。
 尽pipe在java中没有AtomicDouble或AtomicDoubleArray ,你可以很容易地基于AtomicLongArray创build自己的。 
 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)); } } 
 正如你所看到的,我使用Double.doubleToLongBits和Double.longBitsToDouble在AtomicLongArray存储Doubles AtomicLongArray 。 它们都具有相同的位数,所以精度不会丢失(除了-NaN,但我认为这不重要)。 
 在Java 8中, add的实现可以更容易,因为您可以使用在Java 1.8中添加的AtomicLongArray accumulateAndGet方法。 
更新 :看来,我几乎重新实施番石榴的AtomicDoubleArray 。
即使是普通的“double”数据types在32位JVM中也不是线程安全的(因为它不是primefaces的),因为它在Java中需要8个字节(涉及2 * 32位操作)。
 正如已经解释过的,这段代码不是线程安全的。 在Java-8中避免同步的一个可能的解决scheme是使用新的DoubleAdder类,它能够以线程安全的方式保持双数的总和。 
 在并行化之前创buildDoubleAdder对象的数组: 
 DoubleAdder[] adders = Stream.generate(DoubleAdder::new) .limit(typeCount).toArray(DoubleAdder[]::new); 
然后在这样的并行线程中累加和:
 for(int type = 0; type < typeCount; type++) adders[type].add(parts[type]); } 
最后在并行子任务完成后得到结果:
 double[] result = Arrays.stream(adders).mapToDouble(DoubleAdder::sum).toArray();