1. 单例模式介绍

单例模式提供了一种创建对象的最佳形式。
由某类负责创立本人的对象,并提供该对象的拜访形式,以确保该对象是该类惟一的实例。

(1) 实用状况:

存在一个全局应用的类被频繁的创立和销毁时,能够思考应用单例模式。

(2) 长处:

在堆中有且仅有一个实例,防止了频繁创立销毁以及保留的内存开销。

(3) 毛病:

没有接口,不能继承。
它在类的外部创立了本人的对象,这与繁多职责准则抵触,它原本不应该关怀内部是怎么实例化它的。

2. 单例模式实现

(1) 懒汉式(线程不平安)

先来看一种最简略的单例模式:

public class Singleton {    private static Singleton instance;    // 公有构造方法    private Singleton() {    }    public static Singleton getSingleton() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}

为什么叫懒汉,因为它不急着创立实例,而是等到断定为空的时候才去创立

它最大的问题就是线程不平安,因为可能会有多个线程断定instance == null,从而创立多个实例,严格意义上说,它并不能算单例模式。

(2) 懒汉式(线程平安)

上边的实现是线程不平安的,那给getSingleton办法减少synchronized不就行了吗:

public class Singleton {    private static Singleton instance;    // 公有构造方法    private Singleton() {    }    public static synchronized Singleton getSingleton() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}

这样做的确是能够保障线程平安的,然而加锁会影响效率
其实除了第一次须要创立之外,后续的加锁就没有意义了

(3) 饿汉式

饿汉式指的是在类加载的时候,就间接创立了实例:

public class Singleton {    private static Singleton instance = new Singleton();    // 公有构造方法    private Singleton() {    }    public static Singleton getSingleton() {        return instance;    }}

这是比拟罕用的形式,也能够保障线程平安
然而在类加载的时候就实例化的话,万一它很耗费资源,就有点节约内存

(4) 双重校验锁

应用双重校验锁的形式能同时保障线程平安和高性能:

public class Singleton {    // 留神这里的volatile    private volatile static Singleton instance;    // 公有构造方法    private Singleton() {    }    public static Singleton getSingleton() {        // 第一次判断是否为空,为空才创立,否则间接返回        if (instance == null) {            synchronized (Singleton.class) {                // 第二次判断是否为空,避免已被其余线程实例化之后反复实例化                if (instance == null) {                    instance = new Singleton();                }            }        }        return instance;    }}

这种形式中判断了两次instance == null:
第一次判断是否为空,为空才创立,否则间接返回
第二次判断是否为空,避免已被其余线程实例化之后反复实例化
这里的要害是在申明instance变量的时候,减少了volatile关键字

volatile有两个作用:

  1. 保障了不同线程对这个变量进行操作时的可见性,即一个线程批改了某个变量的值,这新值对其余线程来说是立刻可见的。但如果对该变量执行的是非原子操作,它不能保障线程平安
  2. 禁止指令重排序