volatile是JVM提供的轻量级同步机制
- 保证被volatile修饰的共享变量对所有线程总是可见的。当一个线程修改了一个被volatile修饰的共享变量时,其他线程能够立即感知到变动
- 禁止指令重排序优化。
使用volatile修饰的变量仍然会存在线程安全的问题
如下程序所示,两个线程可能在同一时间读取value值为同一值,对value值进行加1的操作,就会导致线程安全的问题。
要解决线程安全的问题,就要在increase方法前加上synchronized关键字。由于synchronized可以实现线程同步,所以可以去掉value的volatile修饰符。
使用volatile实现线程安全的场景
使用volatile修饰的shutdown变量对其他线程立即可见,从而实现了线程安全。
Volatile变量为何立即可见?
当写一个volatile变量时,JVM会把该线程对应的工作内存中的共享变量值刷新到主内存中;当读取一个volatile变量时,JVM会把该线程对应的工作内存置为无效,那么线程就只能从主内存中重新读取共享变量的值。
Volatile如何禁止指令重排优化
内存屏障
- 保证特定操作的执行顺序
通过插入内存屏障指令进制在内存屏障前后的指令执行重排序优化
- 保证某些变量的内存可见性
强制刷新各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本
Volatile就是通过内存屏障来实现其在内存中的可见性和禁止重排优化
例子
在创建instance时,分为三步
由于第二步和第三步不存在数据依赖的关系,这时就会出现指令重排序问题
在多线程情况下就会出现问题。
解决的方式:
使用volatile修饰instance变量,来禁止instance变量被执行指令重排序