当前位置: 代码迷 >> 综合 >> Java 的 volatile 关键字学习
  详细解决方案

Java 的 volatile 关键字学习

热度:95   发布时间:2023-12-26 13:01:29.0

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