volatile关键字剖析

发布时间 2023-08-03 10:54:46作者: 骑驴晒太阳

这里引入一个案例 :实现单例模式的双重检查锁

package com.chunling.cloud.test;

public class Singleton {

private static Singleton instance;

private int value;

private Singleton() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = 42; //
}

public int getValue() {
return value;
}

public static Singleton getInstance() {
if (instance == null) { // 第一次检查,避免不必要的同步
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查,确保只有一个实例被创建
instance = new Singleton();
}
}
}
System.out.println(instance.getValue());
return instance;
}


public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(() -> Singleton.getInstance());
thread.start();
}
}

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

讨论:此案例中的instance实例是否应该被volatile关键字修饰呢?  官方给的答案是要加volatile关键字

讲到这里,先来阐述下啊volatile的作用:

      1.保证各线程对其修饰变量的可见性(通过总线锁/cpu三级缓存锁实现)

    就可见性问题而言:即使不加volatile ,我们的instance实例也能被线程共享(静态成员变量是存储在jvm运行时数据区的方法区【方法区的内容会被线程共享】);

         所以上面代码中加同步上锁后,保证了线程访问的顺序性,所以也保证了对象一定是单例的;

   2.防止指令重排序(这里不是我们java代码的指令)

         如上面示例中的value属性,jvm在初始化instance对象时会经历三步(类似于我们spring的三级缓存的过程),

                    step1:实例化对象instance,此时value的值为0;

                    step2:给instance对象赋值,value=8;

                    step1:给instance和value建立连接,即将value设置为instance的成员变量;

    所以这个过程有可能会造成step1实例化完之后对象返回了,后续线程可能会拿到一个没有初始化的instance对象。

 

但是:经过验证  如果不加volatile关键字的话 实例化出来的对象一样  且对象内部的value值也一样; 

可能是因为我对对象的加载过程还没有深度研究的原因吧,待研究完后再来打脸。。。。