保证一个类仅有一个实例,并提供一个访问它的全局访问点。html
简单来讲,也就是须要保证一个类在整个应用程序的生命周期内,只能存在一个实例(没有也行)。为了达成这个目标,应该要作到如下几点:java
首先,咱们按照前面说的三点,写了如下代码:设计模式
public class Singleton { private Singleton instance; private Singleton(){} public Singleton GetInstance(){ return instance; } }
这样,显然是有问题的。安全
一是获取实例的公开方法getInstance,只是生命成public的,外部在没有Singleton的实例的状况下仍是不能调用,就成了一个悖论了。因此须要把GetInstance方法声明为静态的。一样,因为须要被静态方法调用,同时还要用来保持惟一的实例,instance也须要声明为静态的。多线程
二就是还缺乏了对instance变量的初始化,即对构造器的调用。咱们既能够再声明instance是就进行初始化,也能够在静态代码段中对它进行初始化。并发
因而乎,就有了下面两个版本的实现:oracle
public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton GetInstance(){ return instance; } }
public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton(){} public static Singleton GetInstance(){ return instance; } }
因为这两种方法没有实质上的区别,都是在类被加载的时候就进行了实例的初始化(因此被称为饿汉式)。这也是饿汉式的下的两个特色:性能
因为饿汉式在类加载时就完成了实例化,致使了可能存在性能浪费,因此咱们就考虑看看能不能在类被使用时才被实例化呢。若是据说过『懒加载』这个的词话,应该就会以为这很easy了,在以前的基础上,很轻松就写出了下面代码(懒汉式的单例)。优化
public class LazySingleton { private static LazySingleton instance; private LazySingleton() {} public static LazySingleton GetInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
乍一看,已是能够了,至少在单线程下已经没问题了。可是在多线程的场景下呢,很容易就会产生A、B两个线程同时调用GetInstance方法,A线程先判断instance为null,准备进行实例化,但在实例化以前B线程也进行了instance为null的判断,最终结果是两个线程分别调用了一次私有构造器进行实例化,第二次实例化的结果会将第一次的覆盖掉。线程
因此懒汉式有如下特色:
为了解决上面方法的缺陷,也就是所谓的线程不安全,咱们找到了synchronized这个关键字,也只加了这个关键字,获得下面的代码。
public class LazySingleton { private static LazySingleton instance; private LazySingleton() {} public static synchronized LazySingleton GetInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
与前一种方法相比,在实现上只是在getInstance方法上增长了synchronized关键字,使得GetInstance方法同步,同一时间只能有一个线程执行这个方法,那咱们以前说的线程不安全的问题显然就不在了,这样是否是就完美来呢?世界没那么美好,咱们又引入了新的问题。
因为是在整个GetInstance方法上加锁(同步),可是由于实际上只须要进行一次实例化(也只容许进行一次),因此绝大多数场景下是不须要同步的,因此在并发场景下会致使效率下降,至关于多车道在这里并成单车道了。
既然还有问题,那咱们就来继续进行优化。上面的方法由于把整个GetInstance方法设置为synchronized,因此致使多线程在这里受阻,那咱们把同步的范围缩小一点儿,看看状况会不会好一些。
public class LazySingleton { private static volatile LazySingleton instance; private LazySingleton() {} public static LazySingleton getInstance() { if (instance == null) { synchronized(LazySingleton.class){ if (instance == null) { instance = new LazySingleton(); } } } return instance; } }
优化以后,把锁定范围进行了收缩,只在须要进行初始化实例时才进行同步,以后就再也不进行同步。
这样咱们就获得了一种效率较高,而且线程安全的单例模式的构造方法。
PS. 若是有仔细看代码,您或许会发现咱们在声明instance变量的时候,用了一个volatile关键字,若是须要一些解释的话,能够参考Java中的volatile在使用双层检查实现单例模式的解读,后面我也可能来单独说一下这个。
还有一种使用静态内部类来实现的单例,也被各类推荐。由于内部静态类是要在有引用了之后才会装载到内存的,这样就一样实现了懒加载;同时,静态内部类的静态变量的初始化,也是在被加载时进行的初始化,自然的完成来对进程安全的控制。
public class InnerStaticClassSingleton { private Singleton() {} private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static InnerStaticClassSingleton getInstance() { return SingletonInstance.INSTANCE; } }
To Be Continued