Java中的+ =运算符是否是线程安全的? [英] Is the += operator thread-safe in Java?

查看:153
本文介绍了Java中的+ =运算符是否是线程安全的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找到了以下Java代码。

  for(int type = 0; type< typeCount; type ++)
synchronized(result){
result [type] + = parts [type];
}
}

其中结果部分 double []



<我知道原始类型的基本操作是线程安全的,但我不确定 + = 。如果上面的 synchronized 是必要的,是否有更好的类来处理这样的操作?

解决方案

没有。 + = 操作不是线程安全的。对于任何涉及分配给共享字段或数组元素的表达式而言,它需要锁定和/或适当的事先发生关系链是线程安全的。



(如果字段声明为 volatile ,则之前发生关系存在...但仅限于读取和写入操作。 + = 操作包括读取和写入。这些是单独原子的,但序列不是。而且大多数赋值表达式使用 = 涉及一个或多个读取(在右侧)和写入。序列也不是原子。)



有关完整的故事,请阅读JLS 17.4 ......或者Brian Goetz等人的Java Concurrency in Action的相关章节。


当我知道原始类型的基本操作是线程安全的......


实际上,这是一个不正确的前提:




  • 考虑数组的情况

  • 认为表达式通常由一系列操作组成,并且t帽子一系列原子操作不保证是原子的。






有一个 double 类型的附加问题。 JLS( 17.7 )说:


出于Java编程语言内存模型的目的,对非易失性long或double值的单次写入被视为两个单独的写入:每个32位的一半。这可能导致线程从一次写入看到64位值的前32位,而另一次写入看到第二次32位。



volatile和long值的写入和读取始终是原子的。







在评论中,您问:


那么我应该使用哪种类型来避免全局同步,这会阻止此循环中的所有线程?


在这种情况下(你要更新 double [] ,那里无法与锁或原始互斥锁同步。



如果您有 int [] long [] 你可以用 AtomicIntegerArray AtomicLongArray 替换它们并制作使用这些类的无锁更新。但是没有 AtomicDoubleArray 类,甚至没有 AtomicDouble 类。 / p>

UPDATE - 有人指出Guava提供了 AtomicDoubleArray 类,所以成为一个选项。实际上是一个好的。)



避免全局锁定和大规模争用问题的一种方法可能是划分数组到名义区域,每个区域都有自己的锁。这样,一个线程只需要阻止另一个线程,如果它们使用相同的数组区域。(单个写入器/多个读取器锁定可以elp也是......如果绝大多数访问都是读取。)


I found the following Java code.

for (int type = 0; type < typeCount; type++)
    synchronized(result) {
        result[type] += parts[type];
    }
}

where result and parts are double[].

I know basic operations on primitive types are thread-safe, but I am not sure about +=. If the above synchronized is necessary, is there maybe a better class to handle such operation?

解决方案

No. The += operation is not thread-safe. It requires locking and / or a proper chain of "happens-before" relationships for any expression involving assignment to a shared field or array element to be thread-safe.

(With a field declared as volatile, the "happens-before" relationships exist ... but only on read and write operations. The += operation consists of a read and a write. These are individually atomic, but the sequence isn't. And most assignment expressions using = involve both one or more reads (on the right hand side) and a write. That sequence is not atomic either.)

For the complete story, read JLS 17.4 ... or the relevant chapter of "Java Concurrency in Action" by Brian Goetz et al.

As I know basic operations on primitive types are thread-safe ...

Actually, that is an incorrect premise:

  • consider the case of arrays
  • consider that expressions are typically composed of a sequence of operations, and that a sequence of atomic operations is not guaranteed to be atomic.

There is an additional issue for the double type. The JLS (17.7) says this:

"For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write."

"Writes and reads of volatile long and double values are always atomic."


In a comment, you asked:

So what type I should use to avoid global synchronization, which stops all threads inside this loop?

In this case (where you are updating a double[], there is no alternative to synchronization with locks or primitive mutexes.

If you had an int[] or a long[] you could replace them with AtomicIntegerArray or AtomicLongArray and make use of those classes' lock-free update. However there is no AtomicDoubleArray class, or even an AtomicDouble class.

(UPDATE - someone pointed out that Guava provides an AtomicDoubleArray class, so that would be an option. A good one actually.)

One way of avoiding a "global lock" and massive contention problems might be to divide the array into notional regions, each with its own lock. That way, one thread only needs to block another thread if they are using the same region of the array. (Single writer / multiple reader locks could help too ... if the vast majority of accesses are reads.)

这篇关于Java中的+ =运算符是否是线程安全的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆