主要参考:http://www.javaworld.com/javaworld/jw-04-2003/jw-0425-designpatterns.html
及相应的转帖译文:http://blog.csdn.net/songylwq/article/details/6058771
单例是设计模式(时间长都有点忽略这个概念了,呵呵),也许你会说他是最“简单”的设计模式。
某种程度上是这样的,这个设计模式理解起来不困难。但是,在实现层面上,如果细研究,还是有很多值得思考和注意的地方的。
写个单例,很多人肯定信手拈来:
public class ClassicSingleton {
private static ClassicSingleton instance = null;
protected ClassicSingleton() {
// Exists only to defeat instantiation.
}
public static ClassicSingleton getInstance() {
if (instance == null) {
instance = new ClassicSingleton();
}
return instance;
}
}
看起来不错,还挺“聪明”,“懒”加载的模式。
但是,经验多一些的,很快就发现,有漏洞。多线程环境下可能会被两个线程分别创建出两个实例(不单例)了。
解决线程安全问题,方法也很简单,同步关键字:
public static synchronized ClassicSingleton getInstance() {
if (instance == null) {
instance = new ClassicSingleton();
}
return instance;
}
这样安全是安全了,但效率很低。线程同步的开销比普通的调用要大很多,而对于这个单例的getInstance()方法,实际上只需要在第一次调用时同步即可,唯一的单例实例创建好之后的调用,都大可不必须synchronized。
很自然地,我们想到,不同步整个方法,同步创建对象的代码块吧:
public static /*synchronized*/ ClassicSingleton getInstance() {
if (instance == null) {
synchronized (ClassicSingleton.class) {
instance = new ClassicSingleton();
}
}
return instance;
}
这样行吗?很可惜,不成啊!这样的实现方式还是非线程安全的,道理跟不synchronized一样(这里不细解释了)。
继续补救,同步块里再判断,双保险,OK吗?
public static/* synchronized */ClassicSingleton getInstance() {
if (instance == null) {
synchronized (ClassicSingleton.class) {
if (instance == null) {// double-check
instance = new ClassicSingleton();
}
}
}
return instance;
}
对不起,还是不行。因为Java在给变量赋值前,会先“随便” 给个值,那一“瞬间”,变量是不空(null)的。也就是说,内层的check会在特定的瞬间失效。
所以……别在同步上费劲了,没有好的结果。
实际上,最简单也最有效的方法只是因为我们“误会”了,所以没有使用。
public class Singleton {
public final static Singleton INSTANCE = new Singleton();
private Singleton() {
// Exists only to defeat instantiation.
}
}
类的静态变量天然保证了线程安全,new的操作仅在类被加载时调用一次,可以说这是由类加载机制保证的。
你可能会说,这不是“懒”加载的。其实是误会,类的静态变量只有在首次访问时才进行初始化,也就是说,这个实现本身就是“懒”加载的。
这里顺便提到,一个问题:单例的范围是什么?
怎么回答?我觉得应该是一个类加载器(体系)的范围。可以想象,同一个应用服务器的两个不同的Web应用中,同一个类,肯定是有其各自的单例。如果希望这种更大范围也“单例”,那就需要在类加载器做文章了。
这种场景一般没有什么需要,这里就不深入讨论了。
多线程环境下的单例问题解决了,还有一点特别的,就是序列化。
如果希望实现单例的类实现了java.io.Serializable接口,就会存在被序列化机制破坏单例的情况。在另外一篇博文中有提到:http://sharajava.iteye.com/admin/blogs/1270722
解决方案就是实现readResolve()方法:
class Singleton implements java.io.Serializable {
public final static Singleton INSTANCE = new Singleton();
private Singleton() {
// Exists only to defeat instantiation.
}
private Object readResolve() {
return INSTANCE;
}
}
参考的文章中还有关于“注册”的模式实现的讨论,个人感觉很复杂但用处不大,这里就不细说了。
总之,一个看似简单的单例模式就包含了这么多值得思考的东西,感悟到做技术真的需要非常严谨啊!稀里糊涂,自以为是可不行哦。
分享到:
相关推荐
单例模式单例模式单例模式单例模式单例模式单例模式单例模式单例模式
探索讨论单例模式会不会被垃圾回收机制回收,结论是HotSpot中不会
单例多例
C#单例模式C#单例模式详解C#单例模式详解C#单例模式详解
C++单例设计模式,单例模式 C++单例设计模式,单例模式
单例模式是最简单的设计模式之一,但是对于Java的开发者来说,它却有很多缺陷。在本月的专栏中,David Geary探讨了单例模式以及在面对多线程(multithreading)、类装载器(classloaders)和序列化(serialization)时...
单例模式详解~~单例模式详解~~单例模式详解~~
单例省市级联 级联 单例模式 此例子不仅可以学历单例模式 也可以学习级联下拉的实现方式。
单例模式的几种实现方式Demo
几种单例模式的书写方式
QT 单例
java Singleton单例模式 java Singleton单例模式
这个讲的是单例模式的多种不同实现方式,希望对单例感兴趣的同学看看
细究单例那些你不知道的事(OC).zip
一个简单的java工程,包含注释,一目了然,其中包含了单例模式的所有实现方式,懒汉式,饿汉式,双重校验,枚举,静态内部类等方式实现单例。
作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类譬如每台计算机可以有若干个打印机,但只能有一个Printer,以避免两个打印作业同时输出到打印机...
该程序是QT程序单例的demo程序 使用的是共享内存的方式
在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样有几个好处: 1、某些类创建比较频繁,对于一些大型的对象,这可以节省一笔很大的系统开销。 2、省去了new操作符,降低了系统内存的使用频率...
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。 比如在某个服务器程序中,该服务器的配置信息存放...
java 单例模式