问:说说你对 Java 的 volatile 关键字的理解和使用场景举例?
答:一旦一个并发共享变量(类的成员变量、静态成员变量)被 volatile 关键字修饰就具备了可见性(即一个线程修改了一个变量的值对于另一个线程来说是立即可见的)和有序性(即禁止进行指令重排序),实质是在生成的汇编代码中多了一个 lock 前缀指令。譬如我们经常会使用标记法中断线程,如下:
boolean stop = false;
//线程1
while (!stop) {
//do some thread things...
}
//线程2
stop = true;
这段代码其实大多数时候是可以中断线程 1 的,但是依然存在一定小概率无法中断线程 1,因为每个线程都有自己的工作内存,当线程 1 运行时会对主存的 stop 变量拷贝一份放置到自己的工作内存使用,当线程 2 更改了 stop 变量的值后还未来得及写回主存中而接着做其他事情了,此时线程1可能无法立即感知到 stop 变量的改变而无法中断自己造成错误的逻辑,当我们对 stop 变量添加 volatile 修饰符后就不会存在上面的问题了,因为 volatile 会强制线程修改变量的改变立即回写到主存中,当线程 2 修改 stop 变量值时会导致线程 1 的工作内存中 stop 缓存失效进而主动去主存中重新读取 stop 值。
volatile 有序性的保证有两层含义
当程序执行到 volatile 变量的读或者写操作时在其前面的操作的更改肯定已经全部进行且结果已经对后面的操作可见,在其后面的操作肯定还没有进行,在进行指令优化时不能将对 volatile 变量访问的语句放在其后面执行,也不能把 volatile 变量后面的语句放到其前面执行。
volatile 常见的使用场景为
多线程自定义条件标记变量中断;
单例模式的 double check 等。