走进volatile的世界,探索它与可见性,有序性,原子性之间的爱恨情仇!
内容提要
本文介绍了Java中的volatile关键字,它确保多线程对共享变量的可见性和有序性,但不能保证原子性。通过示例代码,说明了volatile关键字的作用和使用方法。
延伸解读
volatile 的适用边界
volatile 能解决多线程间的可见性和有序性问题,但无法保证原子性。它适用于状态标志等单一变量的读写场景,如示例中的 stop 标志。对于复合操作如 count++,即使变量被 volatile 修饰,仍可能因线程切换导致结果错误,此时需借助 synchronized、Lock 或 AtomicInteger 等机制。
可见性问题的根源与解决
可见性问题源于 CPU 缓存:线程修改共享变量后,其他线程可能仍读取旧缓存值。volatile 通过指示 JVM 每次从主存读取变量,并利用缓存一致性协议使其他 CPU 缓存行失效,从而确保修改立即可见。示例中 stop 变量未加 volatile 时线程无法停止,加上后问题解决。
有序性保障机制
指令重排序可能破坏多线程间的有序性。volatile 通过 happens-before 规则和内存屏障禁止重排序:写操作前后插入 StoreStore 和 StoreLoad 屏障,读操作后插入 LoadLoad 和 LoadStore 屏障。这确保了 volatile 变量读写不会与周围指令重排,从而避免示例中 ready 和 num 赋值顺序错乱导致输出 0 的问题。
Q&A
volatile关键字在Java中有什么作用?
volatile关键字确保多线程对共享变量的可见性和有序性,但不保证原子性。
如何使用volatile解决可见性问题?
通过在共享变量前加上volatile修饰符,可以确保修改后的值对其他线程立即可见。
volatile如何确保有序性?
volatile通过happens-before原则和内存屏障来确保有序性,防止指令重排序导致的问题。
为什么volatile不能保证原子性?
因为volatile只确保可见性和有序性,而复合操作如count++不是原子的,可能导致线程安全问题。
如何解决使用volatile时的原子性问题?
可以使用synchronized、Lock或AtomicInteger等机制来解决原子性问题。
volatile的语义增强是从哪个JDK版本开始的?
自JDK1.5后,volatile的语义增强使其在Java多线程中变得更加重要。