1. 定义
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
2. 结构
单例模式有三个要点:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是结构最简单的设计模式一,在它的核心结构中只包含一个被称为单例类的特殊类。单例模式结构如图所示:
- Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。
3. 代码实现
- 普通单例模式,多线程调用会出现创建了多个对象
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
- 饿汉单例模式,是实现起来最简单的单例类,当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。可确保单例对象的唯一性。结构图和代码如下:
public class EagerSingleton {
private static EagerSingleton eagerSingleton = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return eagerSingleton;
}
}
- 懒汉单例模式,在第一次调用getInstance()方法时实例化,在类加载时并不自行实例化,这种技术又称为延迟加载(Lazy Load)技术,即需要的时候再加载实例,为了避免多个线程同时调用getInstance()方法,结构图和代码如下:
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
}
//锁的范围过大,性能降低
synchronized public static LazySingleton getInstance() {
if (null == instance) {
instance = new LazySingleton();
}
return instance;
}
}
上面的懒汉单例使用synchronzed
进行线程锁,范围过大,影响性能,可以使用双重检查锁定(Double-CheckLocking)来实现懒汉式单例类,代码如下:
class LazySingleton2 {
//volatile保证多线程间共享变量的可见性
private volatile static LazySingleton2 instance = null;
private LazySingleton2() {
}
//双重检查锁定(Double-Check Locking)
public static LazySingleton2 getInstance() {
//第一重判断
if (null == instance) {
//锁定代码块
synchronized (LazySingleton2.class) {
//第二重判断
if (null == instance) {
instance = new LazySingleton2();
}
}
}
return instance;
}
}
饿汉式单例类不能实现延迟加载,不管将来用不用始终占据内存;懒汉式单例类线程安全控
制烦琐,而且性能受影响。
还有一种更好的单例实现方式,称之为Initialization Demand Holder (IoDH)的技术,既可以实现延迟加载,又可以保证线程安全,不影响系统性能.在IoDH中,我们在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance()方法返回给外部使用,实现代码如下:
class Singleton2 {
private Singleton2() {
}
private static class HolderClass {
private final static Singleton2 instance = new Singleton2();
}
public static Singleton2 getInstance() {
return HolderClass.instance;
}
}
4. 优缺点
- 优点
- 单例模式提供了对唯一实例的受控访问。
- 可节约系统资源,对于需频繁创建和销毁的对象单例模式可提高系统的性能。
- 允许可变数目的实例,可私用单例控制相似办法来获得指定个数的对象实例。
- 缺点
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。
- 自动垃圾回收技术可能会导致单例对象状态的丢失。
5. 适用场景
- 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
6. 个人理解
单例模式可确保只有一个实例对象,其提供了实例的全局访问点来获得该实例,为了避免多线程访问的隐患,可使用饿汉单例模式和IoDH技术的懒汉单例模式。