AtomicInteger.incrementAndGet() 根据文档是原子的。但是,在下面的源代码中,如果另一个线程在"返回下一个"之前交错怎么办?那么"下一个"会不正确吗?
public final long incrementAndGet() {
for (;;) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
如果另一个线程交错,那么它也将成功地将一个添加到值中。
原子性保证您的增量发生,如果两个线程尝试增量,那么最终两个线程将成功(并递增两个)。它不保证任何未来价值。
在您的方案中,它看起来像以下代码:
public final long incrementAndGet() {
for (;;) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next)) {
// Other thread.
for (;;) {
long current2 = get();
long next2 = current2 + 1;
if (compareAndSet(current2, next2)) {
return next2;
}
}
return next;
}
}
}
即每个线程都获得自己的递增值。
该值随每次调用正确递增,因此它唯一可能影响的是返回值。
该函数的语义是它不返回整数的当前值,而是返回调用incrementAndGet()
增加整数的值(更新的值)。
我举个例子:
public MyClass {
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
}
现在,如果您调用getNextUniqueIndex()
它将始终返回唯一值,无论调用线程或线程交错。这是因为从incrementAndGet()
的实现中返回的next
值是指此incrementAndGet()
调用更新的值。
如果在compareAndSet
之后,那么是的,增量AndGet可能会向其他线程提供更高的值。然而,返回后也是如此。该值将增加两个(正确),并且两个线程将收到不同的值。
这形成了"正确"的语义。对于并行线程,没有完美的时间同步性。
compareAndSet
方法保证更新的原子性,但不能保证返回的值是"最新"。incrementAndGet
仅保证 1) 该值以原子方式递增,2) 您获得该递增的结果。在成功增量之前(或之后)发生的事情与当前Thread
无关。
"正确"?
下一个值不是 AtomicInteger 的当前值,而是递增"当前"值产生的值。
假设以下模拟(以这种方式,"线程 2"在"返回下一个"之前交错"线程 1")
i = 10
Thread 1
calling 'j = incrementAndGet(i)'
computing incrementAndGet(i) by Thread 1
now i = 11
Thread 2
calling 'm = incrementAndGet(i)'
computing incrementAndGet(i) by Thread 2
now i = 12
Thread 1
now j = 11
Thread 2
now m = 12
Thread 3
calling 'n = incrementAndGet(i)'
// Hey! It should be only with Thread 1 and 2!
now i = 13 and n = 13
事实上,增量和获取是原子的。为什么?因为线程 1 在我 10 岁时调用增量和获取并得到 11。线程 2 在我 11 岁时称为 incrementAndGet,得到 12。我是否会被改变并不重要。在增量和获取之后,调用方在增量后获得结果值。
增量AndGet正在使用compareAndSet,它正在使用unsafe.compareAndSwapInt。此方法以原子方式将 Java 变量更新为给定整数(如果它当前保存预期的整数)。
因此,当两个线程尝试递增该值时,JVM将以原子方式比较现有值,如果它相同,则增量将成功,否则将不会成功。