volatile 关键字可以实现可见性和有序性。
- 可见性:多个线程共享资源,一个线程修改共享资源,另外一个线程可以立即获得这个共享资源。
- 有序性:禁止对指令进行重排序。
1. 可见性
当一个变量被 volatile 关键字修饰,线程A写这个变量,先修改本地内存,但是会立即将刷新到主内存中,对接下来读这个变量的线程发送一个消息(共享变量已被修改,你的本地内存已经失效),其他线程去主内存中去读取这个变量。
如下一段代码,线程B通过修改变量stop中断线程A,这个操作有问题吗?
答案是有的,虽然几率很小,错误的情况:线程B修改了变量stop,还没来得及刷新到内存中,或者线程A一直在读自己的本地变量,线程A 则得不到 变量stop 被修改的消息,线程A就会一直运行下去。
//线程A
boolean stop = false;
while(!stop){doSomething();
}//线程B
stop = true;
2. 有序性
volatile 禁止 JVM 和 处理器对 volatile 关键字修饰的指令重排序,前后与volatile关键字无依赖关系的指令可以随便排序。
例如下面这个样例,在volatile int z = 20; 之前, int x = 0; int y = 1; 的执行顺序无需考虑,这两个操作都必须在volatile int z = 20;发生。 x++; y--; 都必须 在 volatile int z = 20;之后发生
int x = 0;int y = 1;volatile int z = 20;x++;y--;
例如单例模式(Double check方式实现)中:如果不用 volatile 修饰实例 , private static volatile Lazy02Singleton lazy02Singleton; name 的实例化和 instance 的实例化并没有前后关系,成员变量 name 的实例化还没有完成,其他线程可能调用 的 getInstance 获取 instance,这样就会出错:实例还没有正确实例化就被其他线程调用。
package Singleton;/*** 线程安全的懒汉单例模式实现* 懒汉单例模式:在类加载的时候,不创建实例* 优点:类的加载速度快* 缺点:获取唯一可用的对象较慢*/public class Lazy02Singleton {// 私有、静态的 CommonSingleton 对象、 未初始化private static volatile Lazy02Singleton lazy02Singleton;private StringBuilder name;/*** 私有化构造函数,这样该类不能在外部实例化* 只能通过 getInstance() 获取 Lazy01Singleton类 唯一可用的对象*/private Lazy02Singleton() {System.out.println("生成了一个实例");this.name = new StringBuilder("Singleton");}public static Lazy02Singleton getInstance() {if (lazy02Singleton == null) {// 对这个类加锁synchronized (Lazy02Singleton.class) {if (lazy02Singleton == null) {lazy02Singleton = new Lazy02Singleton();}}}return lazy02Singleton;}
}
参考文献 :
- Java高并发编程详解:多线程与架构设计 / 汪文君著——北京:机械工业出版社,2018.5