当DC遇到volatile,真不清楚要搞什么?

public class Singleton {
    private volatile static Singleton instance;

    private Singleton(){}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

先看这个单例模式,这就是所谓的 DCL,这里重点不在说明什么是DCL,这里要说说,为什么 instance要用 volatile来修饰

volatile关键字有两个作用

  1. 保证线程可见性
  2. 防止指令重排序
    在这里插入图片描述
    线程可见性,即线程A修改的某个变量,线程B读取这个值,会读到修改后的值,而不是本地缓存中的值。这里缓存一致,通过两个方法实现。总线加LOCK#锁的方式(锁总线) 或者 通过缓存一致性协议(锁缓存),这两个都是硬件方面的实现。加锁的方式有性能问题,缓存一致性协议,比如MESI是这样实现的。若某对象被volatitle修改后,主内存中就把它设置为invalid状态,线程B读取时,发现是invalid状态,就从主内在中重新读取。

防止指令重排序, 是JVM层级,是通过内存屏障来实现的。并且是读写都有内存屏障
在这里插入图片描述
在这里插入图片描述
明白了这些,来说说DCL那个问题,为什么要用volatile关键字,就是防止指令重排序。1.对象创建先分配内存,2、再初始化相关数据,3、最后将引用指向堆内地址。其中第二个步骤会复杂的多,由于指令重排序的可能,最后一步可能先于第二步执行完成。

问题就来了,第一个线程进来调用,调用创建对象的方法,多线程情况下,其它进来的线程会阻塞。但创建对象的过程中,可能发生指令重排序,第3步先于第2步完成,这时候又有一个线程进来,调用第一个if决断,对象不等于null,直接反一个半初始化的对象。这就是问题,虽然发生的概率很小。加上volatile,杜绝第3步先于第2步完成的情况,这样就不可能返回一个半初始化的对象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章