一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
双重锁定与延迟初始化
之前我们在单例模式中,提及的双重锁定来实现懒汉式,但是双重锁定存在缺陷
将new Singleton()分解
memory = allocate(); //分配内存空间memory
ctorInstance(memory); //对memory进行初始化操作
instance = memory; //变量指向memory地址
经过指令重排后:在没有破坏程序执行结果的情况下,提高执行速度
memory = allocate();
// 变量指向地址和内存初始化操作顺序重排
instance = memory;
ctorInstance(memory);
这样就会导致一种情况:目前instance变量指定了内存地址,即instance!=null; 但是当前内存地址还未初始化。当此时有另一个线程访问时,第一层判断不通过,直接去获取instance,但是当前没有初始化,所以拿到的是错误的东西。
解决方法
volatile
private volatile String instance;
public String getInstance() {
if (instance == null) {
synchronized (SubstituteYuyueService.class) {
if (instance == null) {
instance = new String();
return instance;
}
}
}
return instance;
}
使用volatile修饰变量,那么new语句相当于volatile写(new中只有一个volatile写操作,其他都是普通操作,不涉及volatile变量,因此可以看成一个volatile写) ,new语句的前后就会增加storestore和storeload,防止和volatile读进行重排序。 原理参照 内存屏障
虽然new语句里面的三条语句可能还是会重排序,但是对于new屏障外面的读来说,它只知道最终执行了三条语句的结果,顺序是怎样的并不关心。
基于类初始化
创建类也有一个LC锁。只有一个线程能够拿到锁并创建成功。
而内部静态类只有在调用时才会触发,因此当调用方法创建静态内部类时,LC会阻塞并发,当创建完毕的时候,其他线程就能拿到创建好的类.