Java中volatile的正确测试方式及不同平台运行结果差异原因
两种环境表现不同的核心原因
Java内存模型(JMM)只规定了多线程场景下可见性的最低要求,并没有禁止不同虚拟机实现提供比规范更强的可见性保障,你遇到的差异本质是桌面HotSpot JVM和Android ART虚拟机的JIT编译策略不同导致的。
桌面JVM(Eclipse运行环境)的表现原因
你在Eclipse里用的是标准OpenJDK/OracleJDK的HotSpot虚拟机,它的JIT编译器会做非常激进的优化:对于没有volatile、synchronized等同步修饰的共享变量,当JIT判断该变量不会被当前线程修改时,会把循环内的变量读取操作提升到循环外部,相当于把变量值缓存在寄存器里,后续循环运行时只会读取寄存器的缓存值,不会再同步主内存的最新更新,所以你看到reader线程只读到了第一次修改的1,后续更新都感知不到。
你可以做个简单验证:给Eclipse的运行参数加上-Xint强制关闭JIT、使用纯解释模式运行,你会发现reader线程也能读到所有num的更新,和Android上的表现一致。
Android ART虚拟机的表现原因
Android的ART虚拟机的JIT优化策略和桌面HotSpot差异很大,当前版本的ART不会对这种短生命周期的循环做这类激进的变量提升优化,reader线程每次读取num时都会访问共享内存,所以能拿到最新的值。
但要特别注意:这只是ART当前实现的额外特性,不是规范要求的保证。如果未来ART调整了JIT优化策略,或是你在其他版本的Android系统上运行,这段代码依然可能出现可见性问题。
补充提示
不要依赖特定虚拟机的实现细节编写代码,只要是多线程共享的可变变量,必须严格遵循JMM规范,添加volatile修饰或是使用同步机制保证可见性,否则代码的正确性是没有保障的。
内容的提问来源于stack exchange,提问作者BobTheCat

