首先我们先看看一个例子,在说什么是单利模式(Singleton):
- package org.bestupon.dp.singleton;
- /**
- *
- * @author BestUpon
- * @email bestupon@foxmail.com
- * @date 2010-6-13上午11:08:28
- * @ask jdk中Runtime这个类似就是一个单例模式的应用:
- *
- * @answer
- */
- public class Test4RunTime {
- public static void main(String[] args) {
- Runtime runtime = Runtime.getRuntime();
- runtime.freeMemory();
- }
- }
上面的例子可以看出在没有使用new,却获得了一个Runtime对象,这是为什么呢?让我们看看java.lang.Runtime.java的源码到底是怎么一回事:
- public class Runtime {
- private static Runtime currentRuntime = new Runtime();
- public static Runtime getRuntime() {
- return currentRuntime;
- }
- /** Don't let anyone else instantiate this class */
- private Runtime() {}
- // 以下略
- }
以上是Runtime.java开头部分的代码,我们可以很清楚的看见,一开头就直接 new Runtime(), 一个对象,并且是静态的。在getRuntime()的时候,
直接将其返回给请求的客户端。 上面结构即采用 Singleton 模式设计,其结构使用 UML 描述 如下所示:
Singleton 的英文意义是独身,也就是只有一个人,应用在面向对象语言上,通常翻译作单例:单一个实例( Instance )。 Singleton 模式可以保证一个类别 只有一个实例,并只提供一个访问( visit )这个实例的方法。
1. 定义: 单例模式就是确保一个类中只有一个实例,并且该实例必须自动创建,并向整个系统提供该实例。
2. 使用时机: 当系统要求一个类只有一个实例时,就需要使用用单例模式。
有几个实例上面结构的方法,可以在第一次需要实例时再建立对象,也就是采用所谓的 Lazy Initialization :
- public class Singleton {
- private static Singleton instance = null ;
- private Singleton() {
- // ....
- }
- public static Singleton getInstance() {
- if (instance == null ) {
- instance = new Singleton();
- }
- return instance;
- }
- // .. 其它实例
- }
上面的实例适用于单线程的程序,在多线程的程序下,以下的写法在多个线程的竞争资源下,将仍有可能产生两个以上的实例,例如下面的情况:
- Thread1: if (instance == null ) // true
- Thread2: if (instance == null ) // true
- Thread1: instance = new Singleton(); // 产生一个实例
- Thread2: instance = new Singleton(); // 又产生一个实例
- Thread1: return instance; // 回传一个实例
- Thread2: return instance; // 又回传一个实例
在多线程的环境下,为了避免资源同时竞争而导致如上产生多个实例的情况,加上同步( synchronized )机制:
- public class Singleton {
- private static Singleton instance = null ;
- private Singleton(){}
- synchronized static public Singleton getInstance() {
- if (instance == null ) {
- instance = new Singleton();
- }
- return instance;
- }
- }
不过这种简单的写法不适合用于像服务器这种服务很多线程的程序上,同步机制会造成相当的效能低落,为了顾及 Singleton 、 Lazy Initialization 与效能问题,因而有了 Double-check Locking 的模式:
- public class Singleton {
- private static Singleton instance = null ;
- private Singleton(){}
- public static Singleton getInstance() {
- if (instance == null ){
- synchronized (Singleton. class ){
- if (instance == null ) {
- instance = new Singleton();
- }
- }
- }
- return instance;
- }
- }
也就是只有在第一次建立实例时才会进入同步区,之后由于实例已建立,也就不用进入同步区进行锁定。 Java 中 Runtime 类别的作法简单的多, 它舍弃了 Lazy Initialization ,如果您要取得单例的机会不是很多,可以用这种方式:
- public class Singleton {
- private static Singleton instance = new Singleton();
- private Singleton() {
- // ....
- }
- public static Singleton getInstance() {
- return instance;
- }
- // 其它实例
- }
Singleton 本身的观念简单但应用 很广,因而很多时候必须对实际环境作一些考虑与调整。
3.总结:
单例模式可以分为两种:饿汉式和懒汉式两种,饿汉是在系统启动的一开始就初始化好了实例,而懒汉式是在第一次访问的时候才初始化实例。
- package org.bestupon.dp.singleton;
- /**
- * @author BestUpon
- * @email bestupon@foxmail.com
- * @date 2010-6-13上午11:34:27
- * @ask 饿汉式单利模式
- * @answer
- */
- public class HungerSingleton {
- /**
- * 一开始就初始化好了实例
- */
- private static HungerSingleton instance = new HungerSingleton();
- private HungerSingleton() {
- }
- public static HungerSingleton getInstance() {
- return instance;
- }
- }
package org.bestupon.dp.singleton;
- /**
- *
- * @author BestUpon
- * @email bestupon@foxmail.com
- * @date 2010-6-13上午11:41:22
- * @ask 懒汉式单例模式
- * @answer
- */
- public class LazySingleton {
- private static LazySingleton instance = null ;
- private LazySingleton() {
- }
- public static LazySingleton getInstance() {
- if (instance == null ){
- instance = new LazySingleton();
- }
- return instance;
- }
- }
4.优点:
在单利模式中,客户调用类的实例时,只能调用一个公共的接口,这就为整个开发团队提供了共享的概念,
5.缺点:
单利模式在实例化后,是不允许类的继承的;在分布式系统中,当系统的单利模式类被复制运行在多个虚拟机下时,在每一个虚拟机下都会创建一个实例对象,此时如果想知道具体哪个虚拟机下运行着单例对象是很困难的,而且单例类是很难实现序列化的。
由于端午节放假的缘故,各位同仁在期间提出了很多的问题,也指出了我思维的局限性,没有及时的更新于回复各位好友的,深表遗憾!
针对单例模式的很多用法,前面一直是在所单一线程的问题,本来设计多线程的问题就很少,也对双重锁定这个问题没有深入的五挖掘,导致了犯了今天这样的错误。 之后参考了一些资料,针对各位朋友提出的问题与我自身存在的问题,进行改进! 参考的文章是:IBMDeveloperWorks(中国)的网站上的文章 《 双重检查锁定及单例模式 》 ( http://www.ibm.com/developerworks/cn/java/j-dcl.html )。这篇文章真针对各种问题都有分析,包括可见性等问题。得出了一个结论:双重锁定失效的主要原因是:不同JVM之间的无序写入问题,多线程之间的独占、休眠(记忆复苏)所引起的不同 不问题。
最终本文提出了一个建议: 建 议不要使用“双重锁定”! 一个解决单例模式的方案:
底线就是:无论以何 种形式,都不应使用双重检查锁定,因为您不能保证它在任何 JVM 实现上都能顺利运行。JSR-133 是有关内存模型寻址问题的,尽管如此,新的内存模型也不会支持双重检查锁定。因此,您有两种选择:
- 接受如清单 2 中所示的
getInstance()
方法的同步。
- 放弃同步,而使用一个
static
字段。
选择项 2 如清单 10 中所示:
- /**
- *<span style="font- family: verdana, nsimsun, sans-serif; white-space: normal; line- height: 19px;"><a style="color: #5c81a7;" name="code10">< strong>使用 static 字段的单例实现</strong></a></span>
- **/
- class Singleton
- {
- private Vector v;
- private boolean inUse;
- private static Singleton instance = new Singleton();
- private Singleton()
- {
- v = new Vector();
- inUse = true ;
- //...
- }
- public static Singleton getInstance()
- {
- return instance;
- }
- }