DCL的单例一定是线程安全的吗

Posted amberjava

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DCL的单例一定是线程安全的吗相关的知识,希望对你有一定的参考价值。

读了本文,你会知道,为什么不加volatile关键字的单例模式不是线程安全的

 

有经验的开发者都知道双重锁定检查(DCL,Double Check Lock)的单例是最优秀的,如下文所示:

 1 public class Singleton {
 2     private static Singleton instance = null;
 3     public  static Singleton getInstance() {
 4         if(null == instance) {    // 第一次检查
 5             synchronized (Singleton.class) {
 6                 if(null == instance) {   // 加锁后第二次检查                 
 7                     instance = new Singleton();    
 8                 }
 9             }
10         }
11        
12         return instance; 
13         
14     }
15 }

 

这看上去一切都很完美,无懈可击,但实际上这个 getInstance() 方法并不完美。问题出在哪里呢?出在 new 操作上,我们以为的 new 操作应该是:

  1. 分配一块内存 M;
  2. 在内存 M 上初始化 Singleton 对象;
  3. 然后 M 的地址赋值给 instance 变量。

但是实际上,经过编译器优化后的执行顺序是这样的:

  1. 分配一块内存 M;
  2. 将 M 的地址赋值给 instance 变量;
  3. 最后在内存 M 上初始化 Singleton 对象。

如下图所示,线程A进入<第一次检查>,A先获得Synchronize锁,分配一块内存M,先将M的地址赋值给了 instance 变量, 此刻发生线程切换,线程B检测到instance不为空,直接返回未初始化的instance, 如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。    

技术图片

 

 

 

所以,我们会加对象上加 Volatile关键字,禁止指令重排序,

Volatile的原理,可以参考我的另一篇文章:Java多线程的volatile底层实现原理

 

以上是关于DCL的单例一定是线程安全的吗的主要内容,如果未能解决你的问题,请参考以下文章

单例模式双重检查(DCL)引发的多线程问题

Spring框架中的单例bean是线程安全的吗?

Spring框架中的单例bean是线程安全的吗?

Spring框架中的单例bean是线程安全的吗?

单例模式-DCL

怎么实现一个线程安全的单例模式