volatile 可以保证线程可见性且提供了一定的有序性,禁止指令重排序,但是无法保证原子性。
volatile 在多线程开发中保证了共享变量的“可见性”,不会引起线程上下文的切换和调度。
volatile 是无法保证复合操作的原子性。
如果某个线程对 volatile 修饰的共享变量进行更新,那么其他线程可以立马看到这个更新,这就是所谓的线程可见性。
在程序运行中,会将运行所需要的数据复制一份到 CPU 高速缓存中,在进行运算时 CPU 不再也主存打交道,而是直接从高速缓存中读写数据,只有当运行结束后,才会将数据刷新到主存中。
缓存一致性
- 通过在总线加 LOCK# 锁的方式
它是采用一种独占的方式来实现的,即总线加 LOCK# 锁的话,只能有一个 CPU 能够运行,其他 CPU 都得阻塞,效率较为低下。 - 通过缓存一致性协议
缓存一致性协议(MESI 协议),它确保每个缓存中使用的共享变量的副本是一致的。
当某个 CPU 在写数据时,如果发现操作的变量是共享变量,则会通知其他 CPU 告知该变量的缓存行是无效的,因此其他 CPU 在读取该变量时,发现其无效会重新从主存中加载数据。
有序性
在 Java 内存模型中,为了效率是允许编译器和处理器对指令进行重排序,当然重排序它不会影响单线程的运行结果,但是对多线程会有影响。
Java 提供 volatile 来保证一定的有序性。最著名的例子就是单例模式里面的 DCL(双重检查锁)
编译器重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
处理器重排序。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。