单例模式(改进版)【转】

转载自:

http://blog.csdn.net/caihaijiang/article/details/9154077

一、单例模式

    单例模式:确保一个类只有一个实例,并提供一个全局访问点。
    在java中实现单例模式,需要:私有的构造器、一个静态变量和一个静态方法。类图如下:

二、单例模式实现

  实现方式一:不考虑多线程情况

  1. public class Singleton {  
  2.     private static Singleton uniqueInstance;  
  3.     private Singleton() {  
  4.     }  
  5.     public static Singleton getInstance() {  
  6.         if (uniqueInstance == null) {  
  7.             uniqueInstance = new Singleton();  
  8.         }  
  9.         return uniqueInstance;  
  10.     }  
  11. }  
public class Singleton {
    private static Singleton uniqueInstance;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}
该实现方式,在多线程的情况下,存在问题:会产生多个实例。
实现方式二:考虑多线程,但牺牲了性能
  1. public class Singleton {  
  2.     private static Singleton uniqueInstance;  
  3.     private Singleton() {  
  4.     }  
  5.     public static synchronized Singleton getInstance() {  
  6.         if (uniqueInstance == null) {  
  7.             uniqueInstance = new Singleton();  
  8.         }  
  9.         return uniqueInstance;  
  10.     }  
  11. }  
public class Singleton {
    private static Singleton uniqueInstance;
    private Singleton() {
    }
    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}
跟实现方式一相比,getInstance方法使用synchronized进行了同步。这解决了多线程并发的问题,但是却因为同步而导致每次访问该方法,都付出了性能的代价(同步一个方法可能造成程序执行效率下降100倍)。

当然,如果getInstance()的性能对应用程序不是很关键(例如访问次数很少),则可以采用这种方式,既简单又有效。如果性能是关键(例如该方法被频繁调用),则需要使用下面的两种方式来实现。

实现方式三:使用“急切”创建实例,而不用延迟实例化的做法(也称为“饿汉式”单例类,与其对应的是“懒汉式”单例类)

  1. public class Singleton {  
  2.     private static Singleton uniqueInstance = new Singleton();  
  3.     private Singleton() {  
  4.     }  
  5.     public static Singleton getInstance() {  
  6.         return uniqueInstance;  
  7.     }  
  8. }  
public class Singleton {
    private static Singleton uniqueInstance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstance() {
        return uniqueInstance;
    }
}

利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的实例。其缺点就是,没有延迟实例化,只要类被加载了,就创建实例,而不管后续是否使用该实例。

实现方式四:用“双重检查加锁”,在getInstance()中减少使用同步

利用双重检查加锁,首先检查实例是否已经创建,如果未创建,则进行同步,然后再次判断是否已创建实例,如果没有,则创建。

  1. public class Singleton {  
  2.     private volatile static Singleton uniqueInstance;  
  3.     private Singleton() {  
  4.     }  
  5.     public static Singleton getInstance() {  
  6.         if (uniqueInstance == null) {  
  7.             synchronized (Singleton.class) {  
  8.                 if (uniqueInstance == null) {  
  9.                     uniqueInstance = new Singleton();  
  10.                 }  
  11.             }  
  12.         }  
  13.         return uniqueInstance;  
  14.     }  
  15. }  
public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}
uniqueInstance,定义成volatile,确保实例化的过程中,多线程能够正确的处理该变量。

注意:双重检查加锁不适用于jdk 1.4及更早的版本。


三、JDK中单例模式的应用例子

JDK中的Java.lang.Runtime类,就是使用单例模式,而且使用的是上面介绍的第三种实现方式。如下:

  1. public class Runtime {  
  2.     private static Runtime currentRuntime = new Runtime();  
  3.   
  4.     /** 
  5.      * Returns the runtime object associated with the current Java application. 
  6.      * Most of the methods of class <code>Runtime</code> are instance  
  7.      * methods and must be invoked with respect to the current runtime object.  
  8.      *  
  9.      * @return  the <code>Runtime</code> object associated with the current 
  10.      *          Java application. 
  11.      */  
  12.     public static Runtime getRuntime() {   
  13.     return currentRuntime;  
  14.     }  
  15.   
  16.     /** Don't let anyone else instantiate this class */  
  17.     private Runtime() {}  
  18.       
  19.     // 其他方法省略  
  20. }  
public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance 
     * methods and must be invoked with respect to the current runtime object. 
     * 
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() { 
	return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    
    // 其他方法省略
}

四、小结

1、如果有两个及以上的类加载器,加载Singleton类,仍然会导致Singleton类被实例化出多个对象。所以,如果你的程序有多个类加载器,同时你又使用了单例模式,则需要小心这个问题。
2、单例模式是针对同一个JVM而言的,对于不同的JVM,每个JVM都会创建一个对应的实例。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值