java 并发编程 - 单例模式的实现

136 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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会阻塞并发,当创建完毕的时候,其他线程就能拿到创建好的类.