千家信息网

DCL和Singleton模式的问题怎么解决

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,本篇内容主要讲解"DCL和Singleton模式的问题怎么解决",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"DCL和Singleton模式的问题怎么解决"
千家信息网最后更新 2026年11月04日DCL和Singleton模式的问题怎么解决

本篇内容主要讲解"DCL和Singleton模式的问题怎么解决",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"DCL和Singleton模式的问题怎么解决"吧!

在多线程的情况下Singleton模式会遇到不少问题,一个简单的例子

1:  class Singleton {      2:      private static Singleton instance = null;      3:         4:      public static Singleton instance() {      5:          if (instance == null) {      6:              instance = new Singleton();      7:          }      8:          return instance;     9:      }     10:  }

假设这样一个场景,有两个线程调用Singleton.instance(),首先线程一判断instance是否等于null,判断完后一瞬间虚拟机把线程二调度为运行线程,线程二再次判断instance是否为null,然后创建一个Singleton实例,线程二的时间片用完后,线程一被唤醒,接下来它执行的代码依然是instance = new Singleton();
两次调用返回了不同的对象,出现问题了。

最简单的方法自然是在类被载入时就初始化这个对象:private static Singleton instance = new Singleton();

JLS(Java Language Specification)中规定了一个类只会被初始化一次,所以这样做肯定是没问题的。

但是如果要实现延迟初始化(Lazy initialization),比如这个实例初始化时的参数要在运行期才能确定,应该怎么做呢?

依然有最简单的方法:使用synchronized关键字修饰初始化方法:

public synchronized static Singleton instance() {              if (instance == null) {          instance = new Singleton();      }      return instance;  }

这里有一个性能问题:多个线程同时访问这个方法时,会因为同步而导致每次只有一个线程运行,影响程序性能。而事实上初始化完毕后只需要简单的返回instance的引用就行了。

双检测锁定解决方案

DCL是一个"看似"有效的解决方法,先把对应代码放上来吧:

    1 :   class Singleton {       2 :       private static Singleton instance = null ;       3 :          4 :       public static Singleton instance() {       5 :           if (instance == null ) {    6 :               synchronized (this) {       7 :                   if (instance == null)    8 :                      instance = new Singleton();    9 :              }    10 :          }    11 :          return instance;    12 :      }    13 :  }

用JavaWorld上对应文章的标题来评论这种做法就是smart, but broken。来看原因:

Java编译器为了提高程序性能会进行指令调度,CPU在执行指令时同样出于性能会乱序执行(至少现在用的大多数通用处理器都是out-of-order的),另外cache的存在也会改变数据回写内存时的顺序[2]。JMM(Java Memory Model, 见[1])指出所有的这些优化都是允许的,只要运行结果和严格按顺序执行所得的结果一样即可。

Java假设每个线程都跑在自己的处理器上,享有自己的内存,和共享的主存交互。注意即使在单核上这种模型也是有意义的,考虑到cache和寄存器会保存部分临时变量。理论上每个线程修改自己的内存后,必须立即更新对应的主存内容。但是Java设计师们认为这种约束会影响程序性能,他们试着创造了一套让程序跑得更快、但又保证线程之间的交互与预期一致的内存模型。

synchronized关键字便是其中一把利器。事实上,synchronized块的实现和Linux中的信号量(semaphore)还是有区别的,前者过程中锁的获得和释放都会都会引发一次Memory Barrier来强制线程本地内存和主存之间的同步。通过这个机制,Java中的同步机制保证了synchronized块中指令的原子性(atomic)。

双检测锁定的问题

好了,回过头来看DCL问题。看起来访问一个未同步的instance字段不会产生什么问题,我们再次来假设一个场景:

线程一进入同步块,执行instance = new Singleton(); 线程二刚开始执行getResource();

按照顺序的话,接下来应该执行的步骤是 1) 分配新的Singleton对象的内存 2) 调用Singleton的构造器,初始化成员字段 3) instance被赋为指向新的对象的引用。

前面说过,编译器或处理器都为了提高性能都有可能进行指令的乱序执行,线程一的真正执行步骤可能是1) 分配内存 2) instance指向新对象 3) 初始化新实例。如果线程二在2完成后3执行前被唤醒,它看到了一个不为null的instance,跳出方法体走了,带着一个还没初始化的Singleton对象。

错误发生的一种情形就是这样,关于更详细的编译器指令调度导致的问题,可以参看这个网页 [4]。

[3] 中提供了一个编译器指令调度的证据

instance = new Singleton(); 这条命令在Symantec JIT中被编译成

0206106A   mov         eax,0F97E78h  0206106F   call        01F6B210                  ; 分配空间  02061074   mov         dword ptr [ebp],eax       ; EBP中保存了instance的地址    02061077   mov         ecx,dword ptr [eax]       ; 解引用,获得新的指针地址    02061079   mov         dword ptr [ecx],100h      ; 接下来四行是inline后的构造器  0206107F   mov         dword ptr [ecx+4],200h      02061086   mov         dword ptr [ecx+8],400h  0206108D   mov         dword ptr [ecx+0Ch],0F84030h

可以看到,赋值完成在初始化之前,而这是JLS允许的。

另一种情形是,假设线程一安稳地完成Singleton对象的初始化,退出了同步块,并同步了和本地内存和主存。线程二来了,看到一个非空的引用,拿走。注意线程二没有执行一个Read Barrier,因为它根本就没进后面的同步块。所以很有可能此时它看到的数据是陈旧的。

还有很多人根据已知的几种提出了一个又一个fix的方法,但最终还是出现了更多的问题。可以参阅[3]中的介绍。

[5]中还说明了即使把instance字段声明为volatile还是无法避免错误的原因。

由此可见,安全的Singleton的构造一般只有两种方法,一是在类载入时就创建该实例,二是使用性能较差的synchronized方法。

到此,相信大家对"DCL和Singleton模式的问题怎么解决"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

线程 问题 方法 内存 同步 对象 性能 指令 编译 模式 主存 实例 程序 编译器 调度 接下来 内容 处理器 字段 还是 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 为想互联网科技怎么样 深圳恒合互联网络科技有限公司 互联网科技保险 深圳期待互联网科技 互联网科技公司介绍 云创客互联网科技公司 互联网为啥属于高科技 玉溪互联网科技怎么选 健康食品互联网创新科技园项目 洛阳共同富裕互联网科技有限公司 阿里互联网科技企业 用友工业互联网科技发展 科技互联网的核心竞争力 雨阳互联网卡科技有限公司 图游互联网科技 互联网科技包装股份有限公司上市 广州智享互联网科技 易租星互联网科技有限公司 承德科技产业互联网平台 互联网科技快速发展优美句子 中国 科技 互联网 杭州互联网科技公司 互联网科技小额贷款 成都互联网科技公司100强 互联网科技有哪些公司 佛山市拉法互联网科技有限公司 爱医互联网络科技 互联网金融科技公司的优势 宇信科技在杭州互联网排第几 工业互联网科技创业
0