Java未同步方法为何产生一致结果?新手多线程测试咨询
为什么未同步的线程方法会产生一致结果?
嘿,这个问题挺常见的,刚接触线程同步的时候很容易碰到这种“意外”的一致结果😉。咱们来拆解一下背后的原因:
首先得明确,globalCounter++并不是原子操作——它实际包含三个独立步骤:
- 读取
globalCounter的当前值 - 对读取到的值执行加1操作
- 将计算后的新值写回
globalCounter
理论上,多线程环境下如果没有同步机制,这三个步骤可能被线程调度器打断,导致竞态条件(比如两个线程同时读取到同一个值,各自加1后写回,最终只完成了一次有效的递增)。但你实际运行没出问题,大概率是这几个原因:
1. 循环次数太少,竞态条件没触发
每个线程只执行5次递增,这种低次数下,线程切换刚好没发生在globalCounter++的三个步骤之间的概率很高。你可以试试把循环次数改成10000次,再跑几次,几乎肯定会得到小于15的结果——这时候竞态条件就会显现出来了。
2. 线程执行时序接近串行
可能你的测试环境中,线程调度器刚好让三个线程串行执行:第一个线程跑完5次递增后,第二个才开始,最后第三个执行。这种情况下自然不会有竞态问题,结果就是3*5=15。
你可以在run方法的循环里加个微小的睡眠,强制增加线程切换的概率,比如:
public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1); // 让线程主动让出CPU,增加切换概率 } catch (InterruptedException e) { e.printStackTrace(); } SharedValueHolder.increment(); } }
再运行几次,应该就能看到不一致的结果了。
3. 额外的小建议:正确的同步方式
如果想保证结果始终正确,有两种常见的解决办法:
- 给
increment方法加synchronized修饰符,把它变成原子操作:public static synchronized void increment() { globalCounter++; } - 使用Java并发包中的
AtomicInteger,它本身就是为原子递增设计的:class SharedValueHolder { public static AtomicInteger globalCounter = new AtomicInteger(0); public static void increment() { globalCounter.incrementAndGet(); } }
内容的提问来源于stack exchange,提问作者Marcin Wolniewicz
相关产品推荐
相关产品推荐

