面试时手写按需加载、多线程环境下的单例类,面试官非说得加个volatile

上周去面试,手写单例类

然后我手写一个平时用的比较多的全局设置类:


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Setting {

    private static Setting setting;

    private  static Lock lock = new ReentrantLock();

    private Setting(){

    }

    public static Setting get() {
        if(setting != null) {
            return  setting;
        }

        lock.lock();
        if(setting == null) {
            setting = new Setting();
        }
        lock.unlock();

        return setting;
    }
}

结果面试官非说这单例在JVM指令重排序的情况下会出问题,非说必须为setting对象加一个volatile,当时很懵逼,现在也很懵逼。本人小白,各位大神分析一下?

阅读 3.4k
2 个回答

指令重排序是为了优化指令,提高程序运行效率。
比如instance = new Singleton() 可分解为:

1、memory = allocate();   // 开辟内存空间
2、ctorInstance(memory);  // 构造对象  
3、instance = memory;     // 将instance指向内存空间

在第三行代码不依赖第二行代码的情况下,JVM会发成重排序:

1、memory = allocate();   // 开辟内存空间
2、instance = memory;     // 将instance指向内存空间
3、ctorInstance(memory);  // 构造对象  

重排序之后的代码,在单线程情况下执行没什么影响,多线程的话执行顺序如下:
线程A:

lock.lock();
if(setting == null) {
    //当A线程执行到上述重排序之后的instance = memory;这句时,setting已经不为空,此时线程B进入
    setting = new Setting(); 
}

线程B:

if(setting != null) {
    // 会直接返回没有执行ctorInstance(memory);的实例,即还没有被完全初始化的实例。
    return  setting; 
}

解决方法:
private static Setting setting;改为 private static volatile Setting setting;
volatile是jdk1.5之后加入的特性,可以禁止变量使用重排序。
码字不易,有用望采纳

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题