不。该操作不是线程安全的。它需要锁定和/或适当的“发生之前”关系链,以便任何涉及赋值到共享字段或数组元素的表达式都是线程安全的。+=
(当字段声明为 ,则存在“发生前”关系...但仅限于读取和写入操作。该操作由读取和写入组成。这些是单独的原子,但序列不是。大多数赋值表达式都涉及一次或多次读取(在右侧)和一次写入。该序列也不是原子序列。volatile
+=
=
有关完整故事,请阅读JLS 17.4 ...或Brian Goetz等人的“Java Concurrency in Action”的相关章节。
据我所知,基元类型的基本操作是线程安全的...
实际上,这是一个不正确的前提:
- 考虑数组的情况
- 考虑到表达式通常由一系列操作组成,并且原子操作序列不能保证是原子的。
该类型还有一个附加问题。JLS(17.7)是这样说的:double
“出于Java编程语言内存模型的目的,对非易失性长整型或双精度值的单次写入被视为两次单独的写入:每段32位一次写入。这可能导致线程从一次写入中看到 64 位值的前 32 位,而从另一次写入中看到第二个 32 位的情况。
“易失性长值和双精度值的写入和读取始终是原子的。
在评论中,您问道:
那么我应该使用哪种类型来避免全局同步,全局同步会停止此循环内的所有线程?
在这种情况下(在更新 的情况下),除了与锁或基元互斥体同步之外,没有其他替代方法。double[]
如果你有 或 你可以用 或 替换它们,并利用这些类的无锁更新。但是没有类,甚至没有类。int[]
long[]
AtomicIntegerArray
AtomicLongArray
AtomicDoubleArray
AtomicDouble
(更新 - 有人指出番石榴提供了一个类,所以这将是一种选择。实际上一个好主意。AtomicDoubleArray
避免“全局锁”和大规模争用问题的一种方法可能是将数组划分为概念区域,每个区域都有自己的锁。这样,如果一个线程使用数组的相同区域,则只需阻止另一个线程。(单个写入器/多个读取器锁也可以提供帮助...如果绝大多数访问都是读取的。