我找到了以下 Java 代码。
for (int type = 0; type < typeCount; type++)
synchronized(result) {
result[type] += parts[type];
}
}
其中 result
和 parts
是 double[]
。
我知道基本类型的基本操作是线程安全的,但我不确定 +=
。如果上述 synchronized
是必要的,是否有更好的类来处理这种操作?
最佳答案
没有。 +=
操作不是线程安全的。对于涉及分配给共享字段或数组元素的任何表达式,它需要锁定和/或适当的“之前发生”关系链,才能实现线程安全。
(使用声明为 volatile
的字段,存在“之前发生”关系...但仅在读取和写入操作中。+=
操作包括一个读和一个写。这些是单独的原子,但序列不是。大多数使用 =
的赋值表达式都涉及一个或多个读取(在右侧)和一个写入。那 < em>sequence 也不是原子的。)
要了解完整的故事,请阅读 JLS 17.4 ...或 Brian Goetz 等人的“Java Concurrency in Action”的相关章节。
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
类。
(更新 - 有人指出 Guava 提供了一个 AtomicDoubleArray
类,所以 会 是一种选择。实际上是一个很好的选择。)
避免“全局锁”和大量争用问题的一种方法可能是将数组划分为概念区域,每个区域都有自己的锁。这样,一个线程只需要阻塞另一个线程,如果它们正在使用数组的相同区域。 (单写/多读锁也有帮助......如果绝大多数访问都是读取。)
关于java - += 运算符在 Java 中是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32676771/