我在 Oracle 文档中读到:
- 对于引用变量和大多数,读取和写入是原子
原始变量(除 long 和 double 之外的所有类型)。
(我猜这个功能已经在一些新的 JDK 版本中添加了,因为我曾经认为所有原始变量的读/写都不是原子的)
这是否意味着
AtomicInteger
已被弃用并且不应在新项目中使用?
虽然在 Java 中,单个存储或来自普通int
的单个加载
是原子的,但您不能原子地(比如说)递增它。这样做需要您首先加载该值,然后根据该值计算新值,然后将新值存储回来。但在两次访问之间,另一个线程可能修改了该值。
AtomicInteger
提供类似
getAndIncrement
的操作,无需使用锁即可用于此目的。
long
和
double
除外)的单独读取和写入是原子的,但
AtomicInteger
(以及
java.util.concurrent.atomic
中的其他原子类)提供了更复杂的原子操作。其中包括像
addAndGet(int)
这样的东西,对于原始 int
变量来说根本不是原子的。因此,
int i = 3;
AtomicInteger j = new AtomicInteger(3);
i += 5; // NOT thread-safe -- might not set i to 8
int n = j.addAndGet(5); // thread-safe -- always sets n to 8
(上面的两条注释都假设 i
和
j
在相关语句starts 执行时没有更改,但可能会在执行开始后但完成之前被另一个线程更改。 )
这是否意味着 AtomicInteger 已被弃用并且不应在新项目中使用?不。首先也是最明显的,如果它被弃用,它将被标记为弃用。
此外,AtomicInteger 和原始 int 根本不能互换。有很多差异,但首先想到的是以下三个:
compareAndSet()
,在基元上不可用。
AtomicInteger.getAndIncrement()
与
int++
,也是不同的;前者是原子的,第二个是两个非原子操作在一起。
我猜这个功能已经在一些新的 JDK 版本中添加了,因为我曾经认为所有原始变量的读/写都不是原子的32 位或更小的原语的读取和写入始终是原子的。
AtomicInteger
。我想澄清一下该文件的内容。该文档中使用术语
atomic 的目的与 AtomicInteger
中使用的目的不同。该文件还指出
原子动作不能交错,因此可以放心使用 螺纹干涉。这里指的是
int x;
x = 1; // thread 1
x = 2; // thread 2
System.out.println(x); // thread 3
thread 3
保证看到值
1
或值
2
。但是,对于
long
或
double
,您没有这样的保证。Java 语言规范 规定
就 Java 编程语言内存模型而言, 对非易失性例如,
long
或double
值的单次写入被视为两次 单独写入:每个 32 位半写入一个。这可能会导致 线程看到 64 位值的前 32 位的情况 一次写入,第二个 32 位来自另一次写入。
long x;
x = 0xffff_ffffL; // thread 1
x = 0x7fff_ffff_0000_0000L; // thread 2
System.out.println(x); // thread 3
thread 3
可以查看
thread 1
分配的前 32 位和
thread 2
分配的最后 32 位,从而创建
long
值
7fff_ffff_ffff_ffff
。
double
也会发生同样的情况。使用
long
修改
double
或
volatile
变量可以防止这种行为。