我一直在研究Java API java.util.concurrent.atomic , 特别是 AtomicInteger 类。
方法评论说这些方法是原子的。
以 getAndIncrement()为例:
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
,正如文件记载的那样," 以原子方式将当前值增加一个。"
究竟是什么让这种方法成为原子? 从我所看到的,它完全是非原子的 - 在执行中涉及许多循环, 在执行声明期间
int next = current + 1;
例如, next 的值可以由另一个线程设置。
答案 0 :(得分:2)
原子性在compareAndSet(current, next)
方法中处理。代码执行增量并仅在尚未更改时设置新值,并且以原子方式完成(或者伪造原子行为)。如果从那以后它被改变了,那就再试一次。所以它可能不是原子的,但它就像它一样。
答案 1 :(得分:1)
AtomicInteger使用volatile和amp;的组合。 CAS(比较和交换)用于整数计数器的线程安全实现。
阅读&写入volatile变量具有与使用同步代码块获取和释放监视器相同的内存语义。因此,JMM保证了易变场的可见性。
AtomicInteger类将其value字段存储在volatile变量中,因此它是传统volatile变量的装饰器,但它提供了唯一的非阻塞机制,用于在需要CAS的硬件级别支持(比较和设置)后更新值。在低到中等线程争用下,与同步阻塞增量操作相比,原子更新提供了更高的吞吐量。
这是AtomicInteger类的getAndIncrement()方法的实现。
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}你可以看到没有获取锁来增加值,而是在无限循环中使用CAS来更新新值。
由于AtomicInteger不需要锁定,因此它可用于编写可扩展的应用程序,其中线程争用低至中等。
答案 2 :(得分:0)
如果你看一下没有循环和CAS会发生什么,那么这里的原子将是什么意思。
如果没有synchronized和compare and set(所以不需要循环),你的代码就像
public final int getAndIncrement() {
int current = get();
int next = current + 1;
set(next);
return current;
}
在多线程环境中,可能有两个线程将当前值视为0&尝试将值更新为一个。两个更新但值仅增加一个。显然没有同步,不能保证不会发生这种情况。
所以这就是compareAndSet的帮助,看看在递增到1之前值是否仍为零。因此,如果当前值为零,我们说只将compareAndSet设置为1,否则会失败。考虑到上面两个线程更新值的情况,最多只有一个线程将成功地将值增加到1而其他线程将失败。因此,尊重操作的原子性(“全有或全无”)。
答案 3 :(得分:0)
如果跟踪compareAndSet函数实现一直到汇编代码,你会发现它被翻译成单个汇编指令(cmpxchg for i486,sun jdk中的文件linux_i486.s),这是一条指令进行价值交换,从而提供所需的原子性而不是锁定。