“单例模式”是设计模式当中最简单的一个,它的目的就是确保某个类只存在唯一一个实例,并提供给所有的其他类使用。当需要用到这个单例类的对象时不是使用new来得到一个新的对象,而是使用单例类的一个静态方法(一般是getInstance)获取已经创建好的对象。使用“单例模式”设计的类常常用来管理共享的资源,比如数据库连接、线程池,或者用来保证应用程序的配置文件的对象和其他一些全局共享的资源不会出现多个副本。
如何实现一个单例类的设计?
先看看下边的一段代码:
接下来稍作更改:
上边代码的问题是每当调用getInstance()方法时都会返回一个新的TestSingleton对象,看看下边的代码:
上边的代码似乎满足了“单例模式”的要求,确保某个类只存在唯一一个实例,并提供给所有的其他类使用,但是当上边代码遇到多线程时会出现问题,有可能会创建出多个实例,造成混乱。假如有两个线程,在TestSingleton实例还没有被创建的情况下调用了getInstance()方法,他们的执行顺序假设为>>线程1:A,线程2:A,线程1:B,线程2:B。。。看到了什么,这个流程实例化了两个TestSingleton对象。这里还需要改进,看看最终“单例模式”的代码:
上边代码提到“双重检查锁(DCL)”在1.4及以前的JAVA版本中会失效,所以在1.4及以前的JAVA代码中需要做些改动,有另外两种方法可以选择:
1. 可以直接将getInstance()方法设置为synchronized
2. 将uniqueInstance实例变量直接初始化: private volatile static Singleton uniqueInstance = new Singleton();
以上只是实现了一个没有任何功能的单例类,而我们需要使用的单例类是有一定功能的,除了以上部分代码,其他功能代码与普通类完全一致。
关于volatile变量,IBM网站上一篇文章写到,volatile变量实现了多线程时变量更改对其他线程立即可见,但是不具备synchronized的原子特性。文章讲的比较难于理解,虽然有一篇中文版,但是一些地方翻译过来更难于理解。对于volatile具有可见性但是不具备原子性我的理解是,例如下边代码:
volatile String test = "ok";
if(test.equals("ok"))
{ // 进入这里后,其他线程可能会改变test的值,所以这里接下来做的操作可能是不应该被执行的,会造成程序混乱。
// 在IBM的那篇文章中也说到这样使用volatile变量是禁止的,使用volatile变量还有很多需要注意的地方,祥见下边链接中的文章。
// 这里执行一些操作
}
上边提到的volatile变量,请参考:http://www.ibm.com/developerworks/java/library/j-jtp06197.html?S_TACT=105AGX52&S_CMP=cn-a-j
另外还有一种可以完全避免使用synchronized的初始化实例变量的方法,请参考:http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom 需要注意,这种方法只适合简单的对象初始化,一旦对象构造过程中抛出异常的话使用此种方法当调用getInstance()方法时会报NoClassDefFoundError 错误。
所有实现单例模式的方法,请参考:http://en.wikipedia.org/wiki/Singleton_pattern#Java