Java多线程行为解析:计数器程序输出不一致问题排查
问题原因分析
1. 主线程未等待子线程执行完成
你的main线程在启动t1和t2后,立刻就执行了System.out.println("result is :"+ counter1.count);,完全没等两个子线程完成那1000次自增操作:
- 当注释掉那些额外的
System.out.println时,子线程的启动和执行速度跟不上主线程的打印动作,主线程打印时count还没被修改,所以输出初始值1。 - 当保留那些打印语句时,
System.out.println是同步方法(底层自带synchronized锁),会拖慢线程执行速度,给子线程留出了部分执行时间,所以你能看到一个大于1但小于2001的随机值。
2. count++不是原子操作
count++看起来是一步操作,实际在字节码层面拆成了三步:
- 读取count的当前值
- 对读取到的值做+1运算
- 将新值写回count
多线程同时执行时会触发竞态条件:比如t1刚读到count=100还没写回,t2也读到count=100,两者都加1后写回,最终count只增加了1,而不是预期的2。这就是保留打印时结果随机且永远到不了2001的原因。
3. System.out.println的隐式同步副作用
System.out的println方法内部有同步块,这会带来两个间接影响:
- 线程执行被阻塞,间接拉长了子线程的执行时间,让主线程打印前子线程能完成更多自增。
- 同步操作会触发内存屏障,强制线程把本地缓存的变量值刷新到主内存,同时读取主内存的最新值,所以你能看到count的变化,但这只是偶然的副作用,绝对不能依赖它来保证线程安全。
解决方法
要得到正确结果,需要同时解决「等待子线程完成」和「保证自增原子性」两个问题:
方法1:用join()等待子线程执行完毕
修改main方法,在启动线程后调用t1.join()和t2.join(),让主线程等两个子线程执行完再打印:
t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("result is :"+ counter1.count);
方法2:保证increment()的线程安全
可以用synchronized修饰方法,或者用AtomicInteger替代普通int:
方式A:用synchronized修饰increment
class Counter{ int count=1; public synchronized void increment(){ count++; } }
方式B:使用AtomicInteger
import java.util.concurrent.atomic.AtomicInteger; class Counter{ AtomicInteger count = new AtomicInteger(1); public void increment(){ count.incrementAndGet(); } }
把两种方法结合,就能稳定得到正确结果2001。
内容的提问来源于stack exchange,提问作者Amir Abram
相关产品推荐
相关产品推荐

