通过具体的例子来分析 balance.compareAndSet(currentBalance, currentBalance - amount)
这一步骤的工作原理。
示例场景
假设我们有一个初始余额为 100 的 AtomicInteger
对象 balance
,两个线程 Thread A
和 Thread B
尝试从该余额中分别转出 10 元。
代码片段
private static AtomicInteger balance = new AtomicInteger(100);
public void transfer(int amount) {
while (true) {
int currentBalance = balance.get(); // 获取当前余额
if (currentBalance < amount) {
break; // 如果余额不足,退出循环
}
// 尝试进行比较并设置新的余额
if (balance.compareAndSet(currentBalance, currentBalance - amount)) {
break; // 如果成功,则退出循环
}
}
}
详细分析
初始状态
balance
的初始值为 100。
第一步:两个线程获取当前余额
Thread A
调用balance.get()
,获取当前余额currentBalance = 100
。Thread B
调用balance.get()
,获取当前余额currentBalance = 100
。
第二步:两个线程尝试更新余额
-
Thread A
尝试执行balance.compareAndSet(100, 90)
:compareAndSet
检查当前的balance
值是否等于 100。- 如果
balance
值确实是 100,则将balance
的值设置为 90。 - 如果
balance
值不是 100,则返回false
,表示操作失败。
-
假设
Thread A
成功地将balance
的值从 100 更新为 90,那么balance.compareAndSet(100, 90)
返回true
,Thread A
退出循环。 -
同时,
Thread B
也尝试执行balance.compareAndSet(100, 90)
:compareAndSet
检查当前的balance
值是否等于 100。- 由于
Thread A
已经将balance
的值更新为 90,所以compareAndSet
检查时,balance
的值不再是 100。 - 因此,
balance.compareAndSet(100, 90)
返回false
,表示操作失败。
第三步:Thread B
重新获取余额并尝试更新
-
由于
Thread B
的compareAndSet
操作失败,它会重新执行balance.get()
,此时获取到的currentBalance = 90
。 -
Thread B
再次尝试执行balance.compareAndSet(90, 80)
:compareAndSet
检查当前的balance
值是否等于 90。- 如果
balance
值确实是 90,则将balance
的值设置为 80。 - 如果
balance
值不是 90,则返回false
,表示操作失败。
-
假设此时
balance
值为 90,Thread B
成功地将balance
的值从 90 更新为 80,balance.compareAndSet(90, 80)
返回true
,Thread B
退出循环。
总结
compareAndSet
方法实现了原子性检查和更新操作,确保在并发环境下,只有一个线程能够成功更新 AtomicInteger
的值,而不会出现竞态条件。这种机制保证了在多个线程尝试同时更新共享变量时,每次只有一个线程能够成功,其他线程必须重新获取最新的值并重试,直到成功为止。
compareAndSet
方法的具体步骤如下:
- 获取当前值
currentBalance
。 - 检查当前值是否与期望值相等。
- 如果相等,则更新值并返回
true
。 - 如果不相等,则返回
false
,并重新尝试。