为什么 double-check null 的单例需要 volatile? | LIXI.FUN
0%

为什么 double-check null 的单例需要 volatile?

  1. new 是个多指令的非原子操作
  2. 使用了 synchronized 包裹的块代码是块与块之间的最终行为表现是有序确定的,但在线程内部是否存在指令重排(无序)是不确定的
  3. 第一个 if 并未在 synchronized 里面,所以另一个线程可能拿到一个因 synchronized 块内部的指令重排导致的未完全初始化的对象(先分配地址,后初始化,但外部线程看已经不为 null 了)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Singleton {

    private static /*volatile*/ Singleton INSTANCE;

    public static Singleton getInstance() {
    // 从线程 B 的角度看 INSTANCE 可能已经不为 null 了,但不知道它是否初始化完全
    if (INSTANCE == null) {
    synchronized (Singleton.class) {
    if (INSTANCE == null) {
    // 线程 A 执行到这个地方的时候
    // 线程 B 可能在第一个 if 那里已经可以看到 INSTANCE 不为 null,但 INSTANCE 可能还只是部分初始化
    INSTANCE = new Singleton();
    }
    }
    }
    return INSTANCE;
    }
    }

所以需要 volatile 避免指令重排造成的问题,需要特殊说明下,这里 volatile 的禁止指令重排,并不是禁止 new Singleton() 里的指令重排,而是禁止对 INSTANCE 的读取和写入的整体操作的指令重排,保证在 INSTANCE 初始化完(写操作)以前,不能进行读操作。

觉得有收获就鼓励下作者吧