单例模式-单机模式
如下代码在单机模式下面没有任何问题。,但是多线程的情况下就会存在多个多个实例对象了
public class TestSingle{
public TestSingle(){
System.out.println("TestSingle 构造");
}
public static TestSingle single;
//单例
public static TestSingle getSingleInstance(){
if(single == null){
single = new TestSingle();
}
return single;
}
public static void main(String[] args){
System.out.println(TestSingle.getSingleInstance() == TestSingle.getSingleInstance());
//多线程下面就存在问题了
for(int i=0;i<=10;i++){
new Thread(()->{
TestSingle.getSingleInstance();
}, String.valueOf(i)).start();
}
}
}
单例模式-多线程 DCL模式(double Check Lock 双端检索机制)
这种情况由于java指令重排的优化机制也无法完全保证单例,可能运行一千万次才出现一次。
也就是说,在某一个线程执行第一次检测时,读取到的single不为null时,single的引用对象可能还没有完全初始化。
single = new TestSingle();可以分为如下三部完成(伪代码)
memory = allocate();//1.分配对象内存空间
single (memory );//2.初始化对象
single = memory //3.设置single 执行刚才分配的内存地址,此时single 不为null
步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变,因此这种重排是允许的
那么重排后就存在下面这种情况,先给对象分配了内存空间,那么single不为null,这个是个下一个线程判断的时候发现不为空,直接返回,于是就得到了要给没有初始化的对象。
memory = allocate();//1.分配对象内存空间single ,
single = memory //2.设置single 执行刚才分配的内存地址,此时single不为null,但是对象还没有初始化完成
single (memory );//3.初始化对象
public class TestSingle{
public TestSingle(){
System.out.println("TestSingle 构造");
}
public static TestSingle single;
//单例
public static TestSingle getSingleInstance(){
if(single == null){
synchronized (TestSingle.class){
if(single == null){
single = new TestSingle();
}
}
}
return single;
}
public static void main(String[] args){
System.out.println(TestSingle.getSingleInstance() == TestSingle.getSingleInstance());
//多线程下面就存在问题了
for(int i=0;i<=10;i++){
new Thread(()->{
TestSingle.getSingleInstance();
}, String.valueOf(i)).start();
}
}
}
如何解决
single 加volatile 静止指令重排
public static volatile TestSingle single;