多线程访问AtomicInteger递增计数,结果超预期(21-22)问题排查
问题分析与解决方案:AtomicInteger计数超出预期值
问题根源
虽然AtomicInteger的get()和incrementAndGet()都是原子操作,但判断条件counter.getCount() < 20与递增操作counter.increment()的组合不是原子性的,这会导致竞态条件:
- 多个线程可能同时读取到当前计数为19(均满足
<20的条件) - 这些线程会依次执行递增操作,最终计数会超过20(比如21或22)
举个具体场景:线程A读取到19,准备执行递增;此时线程B也读取到19,同样进入循环。线程A执行后计数变为20,线程B继续执行递增,计数就变成了21。如果有更多线程同时触发这个情况,数值会更高。
解决方案
要解决这个问题,必须把判断+递增的逻辑变成原子操作,以下是两种可行方案:
方案1:使用CAS(Compare-And-Set)原子操作
利用AtomicInteger的compareAndSet方法,将判断和更新合并为一个原子步骤。修改Counter类,增加一个原子递增的方法:
class Counter { private AtomicInteger atomicInteger = new AtomicInteger(0); // 仅当当前值小于max时,才执行递增 public boolean incrementIfLessThan(int max) { int current; do { current = atomicInteger.get(); if (current >= max) { return false; // 已达到最大值,无需递增 } // 尝试CAS更新:如果当前值还是current,就改为current+1 } while (!atomicInteger.compareAndSet(current, current + 1)); return true; } public int getCount() { return atomicInteger.get(); } }
然后修改线程中的循环逻辑:
while (counter.incrementIfLessThan(20)) { // 空循环,直到无法再递增 }
这种方式完全基于原子类的特性,无需显式加锁,性能更优。
方案2:使用同步块保证原子性
通过synchronized关键字,将判断和递增的逻辑包裹在同一个同步块中,确保同一时间只有一个线程能执行这段代码:
class Counter { private AtomicInteger atomicInteger = new AtomicInteger(0); public synchronized void incrementUntil(int max) { while (atomicInteger.get() < max) { atomicInteger.incrementAndGet(); } } public int getCount() { return atomicInteger.get(); } }
线程中直接调用该方法即可:
counter.incrementUntil(20);
这种方式实现简单,适合对性能要求不高的场景。
原代码的其他小问题
Threads类的main方法返回类型应为void(Java标准main方法要求),而非int- 最后返回语句中的变量名错误:
count.getCount()应改为counter.getCount()
内容的提问来源于stack exchange,提问作者Isat
相关产品推荐
相关产品推荐

