请教大家一个 Java volatile 可见性问题

查看 56|回复 1
作者:songche   
以下摘自某教程:
编译优化带来的有序性问题
有序性指的是程序按照代码的先后顺序执行。而编译器为了优化性能,有时候会改变程序中语句的先后顺序。
Java 中经典的案例就是利用双重检查创建单例对象,其中 volatile 就是保证有序性的。
public class Singleton {
    private static volatile Singleton singleton;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
如果没有 volatile ,我们以为的 new 操作应该是:
[ol]
  • 分配一块内存 M ;
  • 在内存 M 上初始化 Singleton 对象;
  • 然后 M 的地址赋值给 instance 变量。
    [/ol]
    但是实际上优化后的执行路径却是这样的:
    [ol]
  • 分配一块内存 M ;
  • 将 M 的地址赋值给 instance 变量;
  • 最后在内存 M 上初始化 Singleton 对象。
    [/ol]
    假设线程 A 先执行 getInstance() 方法,当执行完指令 2 时恰好发生了线程切换,切换到了线程 B 上;如果此时线程 B 也执行 getInstance() 方法,那么线程 B 在执行第一个判断时会发现 instance != null ,所以直接返回 instance ,而此时的 instance 是没有初始化过的,如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。

    问题:线程 A 在 new 之前获取了锁,为啥线程 B 还可以访问?
    查资料有人说经过这两步  1.分配一块内存 M ; 2. 将 M 的地址赋值给 instance 变量; 后就会释放锁,不知道对不对

    singleton, Instance, 线程, volatile

  • strayerxx   
    B 又没进入 synchronized 不需要获取锁,为什么不可以访问
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部