Time:3 hours
从形式上来看,Singleton Pattern是所有模式中最简单的一种,但是细究起来并不像想象中那么简单。
Singleton的实现有多种方式(见下表及文后所附代码示例)。最基本的是第一种实现,这种实现最大的问题就是不支持多线程。《Head First Design Pattern》第五章列举了三种支持多线程的单例模式实现方式(表中第二、三、四种),这三种实现方式在性能上有所差异。
对于第四种双检锁(DCL)方式,文中特别注明“在Java1.4及更早版本中,许多JVM对volatile关键词的实现,会导致双重检查加锁的实效”;博文[3][4]中对这个问题进行了说明:可能出现instance被赋值,但instance指向的对象尚未初始化完成的情况,此时其他线程若读取instance,就会有问题。[4]中的原话如下:
“The problem with DCL is that code of fetching reference to constructed Singleton object does not require synchronization.
Lets say that thread ‘a’ has entered synchronized block, made the instance not null but hasn’t executed the constructor yet. At this stage thread ‘b’ preempts thread ‘a’. Thread ‘b’ checks if the instance is not null, and since its not, it returns reference to a partially constructed Singleton object.”
从JDK 1.5开始,这个问题已经被解决,DCL方式可正常运行。
《Effective Java》中作者又提及另两种实现方式:
第五种 Class holder lazy initialization (见Item 71): 能达到DCL方式一样的功效,但实现更简单。Joshua没有明说,但是从书中他的话来看,对静态域使用延迟初始化(Singleton模式中正是这种情况),应使用这种方式而不是DCL方式。由于这种方式只适用于静态域的情况,DCL方式可在实例域需要延迟初始化时使用。
第六种 Enum singleon (见Item 3): Joshua认为虽然这种方式还没有被广泛采用,但这是实现Singleton的最佳方法(更简洁,自动支持序列化机制,绝对防止多次实例化)。
在网上查找”Class holder lazy initialization ”时,发现已经有人总结过Singleton的这些实现方式[4],写的很清楚(有点小问题:该文介绍DCL方式时,指出了这种方式存在的问题,但未说明JDK1.5开始这个问题已经被解决)。综合这些资料,列了下面的一览表:
特征词 | JDK版本要求 | 是否Lazy Initialization | 是否多线程安全 | 实现难度 | 说明 | |
第一种 | classic | 是 | 否 | 易 | 不要求线程安全 | |
第二种 | use "synchronize" keyword | 是 | 是 | 易 | getInstance()的性能对应用程序不是很关键(比如该方法使用不太频繁) | |
第三种 | eagerly create | 否 | 是 | 易 | 创建该实例负担不太繁重; 该实例被用到的可能性较大 | |
第四种 | 双检锁 (double-checked locking) | since 1.5 | 是 | 是 | 较复杂 | getInstance()的性能对应用程序很关键; |
第五种 | Class holder lazy initialization | 是 | 是 | 一般 | ||
第六种 | Enum singleton | since 1.5 | 否 | 是 | 易 | 自动支持序列化;不能通过reflection attack来调用私有构造方法 |
(说明:由于时间关系,博主未对本文的内容进行编程验证,相关结论根据阅读的理解整理而来,如有错误欢迎指正)
References
[1] Eric Freeman, etc. Head First Design Pattern.
[2] Joshua Bloch. Effective Java. 2nd.
[3] 西坪. Java中的Volatile关键字. 2010-12-13. url
[4] InitBinder. Singleton Pattern and Problem With Double Checked Locking. 2008-09-06. url