Java共享变量可见性疑问:无同步下线程总能读取更新值?
你没理解错,只是踩了“并发可见性的概率陷阱”!
兄弟,我当初刚学Java并发的时候也跟你一样,对着书里的结论做测试,结果每次都能看到变量更新,差点怀疑书是不是写错了😂。其实你对《Java并发编程实战》里的内容理解完全没错——没有同步机制的情况下,JVM完全不保证一个线程的修改能被另一个线程看到,但这可不是说“一定看不到”,而是结果完全不可控,你的测试只是刚好每次都撞上了“能看到”的情况而已。
为什么你的测试会“看起来正常”?
这里有几个常见的原因:
- CPU缓存的巧合刷新:现代CPU的缓存不是永久保存数据的,当缓存满了、线程被操作系统调度暂停、或者有其他触发缓存同步的操作时,修改的值会被刷回主存,刚好被另一个线程读到。你的测试场景可能太简单,线程执行速度快,缓存还没来得及“留住”旧值就同步了。
- 隐性的同步操作:如果你的测试代码里有打印(比如
System.out.println())、Thread.sleep()或者join()这类操作,其实它们内部都带有同步逻辑,会间接触发内存屏障,强制刷新缓存,让变量值变得可见——只是你没意识到这些操作在帮你做同步而已。 - 简单场景下的JVM优化:当你的测试只有两个线程做简单的读写时,JVM可能会做一些指令优化,甚至在某些情况下把变量直接缓存在寄存器里的概率很低,导致每次都能读到最新值。但一旦把场景变复杂(比如给线程加大量循环、在多核CPU上跑高并发),你就会发现偶尔会出现读取到旧值的情况。
关键要记住:可见性问题是概率性的
并发编程里的可见性BUG最恶心的地方就是它不是必然出现——测试环境、开发机器可能因为CPU核心少、并发量低,永远碰不到,但到了生产环境的多核高并发场景下,就会突然爆发,而且很难复现和排查。
正确的做法
不管测试看起来多“正常”,只要是多线程共享变量的读写,就必须用合法的同步机制来保证可见性:
- 用
synchronized关键字包裹读写操作 - 给共享变量加上
volatile修饰符(适合单一赋值、没有复合操作的场景) - 使用
java.util.concurrent包下的原子类(比如AtomicInteger)
内容的提问来源于stack exchange,提问作者SergeiK
相关产品推荐
相关产品推荐

