首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

单例到底是咋回事

2012-09-19 
单例到底是怎么回事主要参考:http://www.javaworld.com/javaworld/jw-04-2003/jw-0425-designpatterns.htm

单例到底是怎么回事

主要参考: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-checkinstance = 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;}}
?

?

参考的文章中还有关于“注册”的模式实现的讨论,个人感觉很复杂但用处不大,这里就不细说了。

?

总之,一个看似简单的单例模式就包含了这么多值得思考的东西,感悟到做技术真的需要非常严谨啊!稀里糊涂,自以为是可不行哦。

?

?

?

热点排行