Java多线程中AtomicBoolean与Boolean的同步差异问题
线程交替打印中三种同步变量的差异解析
1. AtomicBoolean 为什么能正常运行
AtomicBoolean是Java并发包提供的原子类,它的所有读写操作(比如get()、set()、getAndSet())都是原子性的,而且天然保证了变量的可见性。即使你用synchronized包裹业务逻辑,AtomicBoolean的原子操作加上同步块的互斥,完全避免了竞态条件。两个线程每次读取和修改isOddTurn时,都能拿到最新的正确值,自然能交替执行打印逻辑。
2. volatile Boolean作为参数传递时卡住的核心原因
问题出在Boolean是不可变类,以及Java的参数传递规则上:
- 当你把Boolean作为参数传递给Runnable实现类时,传递的是引用的副本。
- 当你执行
isOddTurn = !isOddTurn时,因为Boolean不可变,实际上是创建了一个新的Boolean对象(比如从Boolean.TRUE变成Boolean.FALSE),原来的引用指向的对象根本没变。 - 这就导致两个线程持有的是不同的Boolean对象引用:线程A修改的是自己手里的引用指向,线程B始终拿着最初的那个引用,根本感知不到线程A的修改。即使加了volatile,volatile修饰的是原来的变量,但线程B看不到新的引用对象,会一直等待isOddTurn变为预期值,最终程序卡住。
3. volatile Boolean设为全局变量时正常运行的原因
全局变量的情况下,两个线程共享同一个变量引用:
- 虽然Boolean不可变,但修改时是让全局变量指向新的Boolean对象。
- volatile关键字保证了这个引用的可见性:一个线程修改了全局变量的引用指向后,另一个线程能立即看到这个新的引用。
- 再加上
synchronized块的互斥,线程每次进入同步块都会读取最新的volatile变量值,能正确判断自己是否该执行打印,所以程序能正常交替输出。
关键总结
synchronized保证的是块内代码的原子性和可见性,但如果变量本身的引用被替换,且线程持有的是旧引用,synchronized也无法解决可见性问题。AtomicBoolean从根本上避免了引用替换的问题(它的内部是用volatile int实现的,修改的是int值而非引用),而全局volatile Boolean则通过共享同一引用让线程能感知到变化,参数传递的volatile Boolean则因为引用副本导致线程各自为政。
内容的提问来源于stack exchange,提问作者Ladu anand
相关产品推荐
相关产品推荐

