建立型模式-单例模式(确保对象惟一性)

1. 定义

单例模式(Singleton Pattern):确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象建立型模式。git

2. 结构

单例模式有三个要点:一是某个类只能有一个实例;二是它必须自行建立这个实例;三是它必须自行向整个系统提供这个实例。单例模式是结构最简单的设计模式一,在它的核心结构中只包含一个被称为单例类的特殊类。单例模式结构如图所示:设计模式

单例模式

  • Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户能够访问它的惟一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,做为外部共享的惟一实例。

3. 代码实现

  1. 普通单例模式,多线程调用会出现建立了多个对象
public class Singleton {

    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

}
  1. 饿汉单例模式,是实现起来最简单的单例类,当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的惟一实例将被建立。可确保单例对象的惟一性。结构图和代码以下:

饿汉单例

public class EagerSingleton {

    private static EagerSingleton eagerSingleton = new EagerSingleton();

    private EagerSingleton() {
    }

    public static EagerSingleton getInstance() {
        return eagerSingleton;
    }

}
  1. 懒汉单例模式,在第一次调用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. 优缺点

  • 优势
  1. 单例模式提供了对惟一实例的受控访问。
  2. 可节约系统资源,对于需频繁建立和销毁的对象单例模式可提升系统的性能。
  3. 容许可变数目的实例,可私用单例控制类似办法来得到指定个数的对象实例。
  • 缺点
  1. 因为单例模式中没有抽象层,所以单例类的扩展有很大的困难。
  2. 单例类的职责太重,在必定程度上违背了“单一职责原则”。
  3. 自动垃圾回收技术可能会致使单例对象状态的丢失。

5. 适用场景

  1. 系统只须要一个实例对象,如系统要求提供一个惟一的序列号生成器,或者须要考虑资源消耗太大而只容许建立一个对象。
  2. 客户调用类的单个实例只容许使用一个公共访问点,除了该公共访问点,不能经过其余途径访问该实例。

6. 我的理解

单例模式可确保只有一个实例对象,其提供了实例的全局访问点来得到该实例,为了不多线程访问的隐患,可以使用饿汉单例模式和IoDH技术的懒汉单例模式。性能

参考

  1. Java设计模式-刘伟
相关文章
相关标签/搜索