You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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++看起来是一步操作,实际在字节码层面拆成了三步:

  1. 读取count的当前值
  2. 对读取到的值做+1运算
  3. 将新值写回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 18:52:51