volatile 介绍
volatile 是 Java 提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块或者说同步方法和 volatile 变量,相比于 synchronized 重量级锁,volatile 更轻量级,因为它不会引起线程上下文的切换和调度。但是 volatile 变量的同步性较差,而且其使用也更容易出错。
volatile 保证可见性,不保证原子性
可见性
当要给线程写 volatile 变量时,JMM 会把该线程本地内存中的变量立即刷新到主内存中去,这个操作会导致其他线程中的 volatile 变量缓存无效。
原子性
一个变量被 volatile 修饰,两个线程都想对这个变量修改,都获取了最新的值,对其进行自增操作也就是 i++,i++ 的过程可以分为三步,首先获取 i 的值,其次对值进行加 1,最后将得到的新值写会到缓存中。那么最后的结果是增加了 1,而不是正确的 2。所以,如果一个变量被 volatile 修饰了,那么肯定可以保证每次读取这个变量值的时候得到的值是最新的,但是一旦需要对变量进行自增这样的非原子操作,就不会保证这个变量的原子性了。
总结
Java 中只有对基本类型变量的赋值和读取是原子操作,如 i = 1 的赋值操作,但是像 j = i 这样的操作都不是原子操作,因为进行了多次原子操作,比如先读取 i 的值,再将 i 的值赋值给 j,两个原子操作加起来就不是原子操作了。
指令重排
重排序是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。重排序需要遵守一定规则:
1、重排序操作不会对存在数据依赖关系的操作进行重排序。
2、重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变。
volatile 禁止指令重排
若用volatile修饰共享变量,在编译时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序,volatile禁止指令重排序也有一些规则:执行到 volatile 变量时,其前面的所有语句都执行完,后面所有语句都未执行。且前面语句的结果对 volatile 变量及其后面语句可见。