参看:http://blog.youkuaiyun.com/lovelion/article/details/7420889
一、模式描述
单例模式(Singletion Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
结构图:
角色:
Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例。
各种单例模式的实现:
单例模式必须保证实例在系统中的唯一性,防止创建多个对象,是单例模式的一个重要目标。为了防止创建多个对象,出现了不同的实现方法。
<1>、饿汉式单例
饿汉式单例在类加载时就创建单例对象,因此后续的使用就不可能创建多个对象。代码如下:
class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() { }
public static EagerSingleton getInstance() {
return instance;
}
}
<2>懒汉式单例与双重检查锁定(Double-Check Locking)
懒汉式单例在第一次调用getInstance()方法时实例化,同时对getInstance()方法进行同步控制,从而保证对象的唯一性。
例如:直接用synchronized关键字修饰getinstance()方法,代码如下:
class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
synchronized public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
上述代码的问题是每次调用getInstance()方法时都要进行线程锁定判断,在多线程高并发访问环境中,将会导致系统性能大大降低。
使用双重检查锁定(Double-Check Locking)来改进懒汉式单例,如下:
class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton() { }
public static LazySingleton getInstance() {
//第一重判断
if (instance == null) {
//锁定代码块
synchronized (LazySingleton.class) {
//第二重判断
if (instance == null) {
instance = new LazySingleton(); //创建单例实例
}
}
}
return instance;
}
}
注意:静态成员变量instance之前增加修饰符volatile。
<3>、饿汉式与懒汉式的比较
饿汉式单例类在类被加载时就将自己实例化,它的优点是无须考虑多线程访问问题,系统加载时需要创建对象,加载时间可能会比较长。
懒汉式单例类在第一次使用是创建,实现了延迟加载。需要多线程机制控制同步,影响系统运行性能。
<4>IoDH单例实现——最好的Java语言单例模式
通过 Initialization Demand Holder(IoDH)技术,实现延迟加载并且减少同步控制的单例类,(在单例类中增加一个静态内部类,在该内部类中创建单例对象,再将单例对象通过getInstance()方法返回给外部使用)代码如下:
//Initialization on Demand Holder
class Singleton {
private Singleton() {
}
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
public static void main(String args[]) {
Singleton s1, s2;
s1 = Singleton.getInstance();
s2 = Singleton.getInstance();
System.out.println(s1==s2);
}
}
通过使用IoDH,可以实现延迟加载(因为Singleton类加载时不会加载静态内部类,而是当第一次调用getInstance()时将加载内部类HolderClass,此时会实例化Singleton),又可以保证线程安全(因为仅在静态内部类加载时初始化一次,由Java虚拟机来保证其线程安全性),不影响系统性能,是最好的Java语言单例模式实现方式。
二、模式优缺点
主要优点:
<1>、单例模式提供了对唯一实例的受控访问。
<2>、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
<3>、允许可变数目的实例。基于单例模式我们可以扩展,使用与单例控制相似的方法来获得指定个数的对象实例。
主要缺点:
<1>、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
<2>、单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身功能融合在一起。
<3>、现代面向对象语言提供了自动垃圾回收技术,如果实例化的共享对象长时间不被利用,系统会认为是来及,会自动销毁并回收资源,下次利用时又将重新实例化,导致共享的单例对象状态的丢失。
”
三、适用场景
<1>、系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
<2>、客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
四、案例分析