JAVA单例类的Double checked机制

          原文题目《Double Checked Locking on Singleton Class in Java,选自Java Code Geeks

       单例类的使用在JAVA开发者中很常见,然而单例类也给初级开发者带来了许多挑战。其中,最主要的问题是如何保证单例类在任何情形下都保持单实例?Double chencked 机制是一种确保类在应用生命周期里只会被创建一个实例类的方法,正如其名称,Double checked机制会在同步锁内外检测两次,以确保单例类只会有一个实例被创建。不过要注意的是,Double checked机制在JDK1.5前由于JAVA内存模型问题任然无法保证真正的单例。在本文中,我们将会看到JAVA里如何编写double checked机制下的单例类,为什么JAVA 5之前double checked机制有缺陷以及它是怎样被修复的。另外,从技术面试的角度看,Double checked的理解也很重要,我曾听说过在金融和服务业的面试中,要求被试手写出带有double checked机制的单例类,相信我面对这样的问题除非你很清楚知道自己在做什么,否则会有很棘手的问题。你也可以尝试下我准备好的一系列单例设计模式的问题。

为什么单例类需要Double Checked机制?

        有一种普遍的情况是多线程下单例类会违反“单例”机制,如果你让一个新手程序猿写出单例模式,大多数情况下他们写出来的代码类似下图所示:

1 private static Singleton _instance;
2
3 public static Singleton getInstance() {
4         if (_instance == null) {
5             _instance = new Singleton();
6         }
7         return _instance;
8 }

        当你指出上面的代码在一个以上的线程并行调用时会创建出多个实例,新手程序猿很可能会在整个getInstance()方法上添加synchronized关键字,就如我们后面提供的第二个代码版本(2nd)所示,尽管这是一个线程安全并且解决了上面多实例问题的方法,但是这样做并不是非常有效率,原本只有当单例类未创建且被访问的时候启用同步锁,然而你却不得不忍受每次访问getInstance()方法都要进行线程锁的判定,大大降低了程序性能。下面的JAVA代码描述了本文所指的Double checked机制,它仅在代码中最需要的部分加上了同步锁,大家称它为Double Checked是因为代码中在同步锁前和锁后用两次 _instance == null 的检查。

01 public static Singleton getInstanceDC() {
02         if (_instance == null) {                // Single Checked
03             synchronized (Singleton.class) {
04                 if (_instance == null) {        // Double checked
05                     _instance = new Singleton();
06                 }
07             }
08         }
09         return _instance;
10 }
Singleton Design Pattern
表明上看来,上述的Double Checked方法非常完美,它仅在需要的地方加上了同步锁,然而上面的方法需要在变量_instance声明中加入volatile关键字,否则上述方法仍然存在缺陷。 没有volatile修饰的情况下,其他的线程可能会拿到未完全初始化的变量_instance,在JAVA 5及其后续版本中,如果添加了volatile关键词修饰,就可以确保任何线程读变量_instance前,该变量已经得到完全的写操作(初始化),这也是为什么JAVA 5之前的Double Checked机制仍然存在漏洞的原因。现在,有了volatile修饰符,我们可以确信代码正确工作了,但是这样的方式并不是线程安全的单例类创建的最好方法,实际上还有两个更好的方式供选择:1. 使用枚举作为单例类(实例创建过程中提供了内置的线程安全性);2.使用static holder 模式。

01 /*
02  * A journey to write double checked locking of Singleton class in Java.
03  */
04  
05 class Singleton {
06  
07     private volatile static Singleton _instance;
08  
09     private Singleton() {
10         // preventing Singleton object instantiation from outside
11     }
12  
13     /*
14      * 1st version: creates multiple instance if two thread access
15      * this method simultaneously
16      */
17  
18     public static Singleton getInstance() {
19         if (_instance == null) {
20             _instance = new Singleton();
21         }
22         return _instance;
23     }
24  
25     /*
26      * 2nd version : this definitely thread-safe and only
27      * creates one instance of Singleton on concurrent environment
28      * but unnecessarily expensive due to cost of synchronization
29      * at every call.
30      */
31  
32     public static synchronized Singleton getInstanceTS() {
33         if (_instance == null) {
34             _instance = new Singleton();
35         }
36         return _instance;
37     }
38  
39     /*
40      * 3rd version : An implementation of double checked locking of Singleton.
41      * Intention is to minimize cost of synchronization and  improve performance,
42      * by only locking critical section of code, the code which creates
43  instance of Singleton class.
44      * By the way this is still broken, if we don't make _instance volatile,
45  as another thread can
46      * see a half initialized instance of Singleton.
47      */
48  
49     public static Singleton getInstanceDC() {
50         if (_instance == null) {
51             synchronized (Singleton.class) {
52                 if (_instance == null) {
53                     _instance = new Singleton();
54                 }
55             }
56         }
57         return _instance;
58     }
59 }

        JAVA中使用枚举实现线程安全的单例类是存在争议的,相比之下有一些更有效的方法来实现单例模式,因此不建议用户自己使用枚举的方法实现JAVA中的单例类。但是这些不建议使用方法的存在有着历史意义,它们揭示出并发情况下是如何引入一些微妙的错误。正如我前面所说,掌握单例模式在面试中也很重要,在任何JAVA面试之前练习写出Double Check机制的单例类可以纠正这类错误见解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值