volatile的两大作用
保持线程的可见性
可见性:当一个线程修改一个共享变量时,其他的线程可以都可以直到到这个值被修改了。
在每次访问 volatile
变量时,都会直接从主内存中读取最新值,而不会使用线程本地的缓存,从而避免了由于线程间的数据不一致而引发的问题。
防止指令重排序
指令排序:是现代处理器为了提高执行效率而进行的一种优化手段。在单线程环境下,指令重排序不会影响程序执行的最终结果,因为最终的执行结果是一致的。但在多线程环境下,指令重排序可能导致线程间的执行顺序与预期不符,从而引发程序逻辑错误或者并发问题。
具体来说,使用 volatile
关键字修饰的变量,在写操作后会插入一个写屏障,而在读操作前会插入一个读屏障。这些屏障指令会阻止指令重排序,确保在多线程环境下,即使不同线程对 volatile
变量进行访问,其执行顺序仍然符合程序的逻辑顺序。
volatile的两大原则
Lock前缀指令会引起处理器缓存写回到内存
- 当一个线程写一个 volatile 变量时,Java虚拟机会向处理器发送一条Lock前缀的指令,这会导致将该变量所在缓存行的数据写回到主内存中。
- 这保证了对 volatile 变量的写操作对其他线程是可见的,因为其他线程从主内存中读取数据时能看到最新的值。
一个处理器的缓存写回到内存会导致其他处理器的缓存无效
- 当一个处理器将 volatile 变量写回到主内存后,会导致其他处理器中缓存了该变量的数据失效。
- 这是因为处理器在缓存一致性协议的约束下,当一个处理器写回数据到主内存后,其他处理器必须将相应的缓存行失效,从而保证所有处理器对该变量的访问都是一致的。