//new:创建一个新对象
//dup:复制之前分配对象空间的引用并压入栈顶
//invokespecail:调用实例方法(此处调用的是类的init方法)
//putstatic:将栈顶元素赋值给静态成员
public class Sington{
//volatile禁止指令重排
private static volatile Sington instance=null;
private Sington(){};
public static Sington getInstance(){
if(instance==null){
synchronized(Sington.class){
if(instance==null) {
//静态引用的赋值操作分为4个指令:new dup invokespecial putstatic
instance = new Sington();
//由于指令重排,所以这个操作有可能会是这样:new dup putstatic invokespecial
//所以如果不加volatile时存在一种情况:当对象未初始化完毕时,instace就非空了
//然后会导致其他线程获取到一个未初始化的对象
}
}
}
return instance;
}
}
- 好久没有复习单例模式,面试的时候竟然连双重校验锁的概念都模糊了。面试官问为什么需要判断两次instance竟然都回答的很勉强。因此在此记录一下
- 第一次在同步块外面判断instance是否已经实例化是为了避免读操作频繁加锁。同步块内部判断instance是否已经实例化是因为在统一时刻可能存在多个线程已经执行过第一个if判断,因此需要通过同步块内部的if判断instance是否已经实例化,避免多个线程创建多个实例