否。=操作不是线程安全的。它需要锁定和/或一个适当的链“happens-before”关系,任何涉及到共享字段或数组元素的表达式都是线程安全的。
(使用声明为volatile的字段,“happens-before”关系存在…但仅在读取和写入操作。=操作由读取和写入组成,它们分别是原子的,但序列不是。大多数赋值表达式using =涉及一个或多个读(在右手边)和一个写,这个序列也不是原子的。
对于完整的故事,请阅读JLS 17.4 …或Brian Goetz等人的“Java并发在行动中”的相关章节。
As I know basic operations on primitive types are thread-safe …
其实,这是一个不正确的前提:
>考虑数组的情况
>认为表达式通常由一系列操作组成,并且原子操作的序列不能保证是原子的。
double类型还有一个问题。 JLS(17.7)说:
“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.”
在评论中,你问:
So what type I should use to avoid global synchronization, which stops all threads inside this loop?
在这种情况下(在更新double []时,除了与锁或原语互斥体同步之外,没有其他选择。
如果你有一个int []或long [],你可以用AtomicIntegerArray或AtomicLongArray替换它们,并利用这些类的无锁更新。但是没有AtomicDoubleArray类,甚至没有AtomicDouble类。
(UPDATE – 有人指出,Guava提供了一个AtomicDoubleArray类,所以这将是一个选项,实际上是一个好的。)
避免“全局锁”和大量争用问题的一种方法可能是将数组划分为概念区域,每个区域都有自己的锁。这样,一个线程只需要阻塞另一个线程,如果他们使用数组的相同区域。 (单个写入器/多个读取器锁可以帮助太…如果绝大多数访问是读取。)