Java 中单例模式是一种常见的设计模式,几乎 设计模式面试题中必问的,开始以前咱们先了解一下为何须要单例模式。java
为何须要单例模式面试
单例模式确保某个类只有一个实例,并且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具备资源管理器的功能。每台计算机能够有若干个打印机,但只能有一个Printer Spooler,以免两个打印做业同时输出到打印机中。每台计算机能够有若干通讯端口,系统应当集中管理这些通讯端口,以免一个通讯端口同时被两个请求同时调用。总之,选择单例模式就是为了不不一致状态。设计模式
单例模式特色缓存
单例类只能有一个实例;安全
单例类必须本身建立本身的惟一实例;多线程
单例类必须给全部其余对象提供这一实例。this
单例模式的写法有好几种,这里介绍经常使用的几种单例模式:懒汉式单例、饿汉式单例、内部类式、枚举式、双重校验式单例。spa
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
写法评价:线程
优势:延迟加载很明显;设计
缺点:致命缺点的是线程不安全,在多线程不能正常工做。
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
写法评价:
优势:线程安全,多线程中很好的工做,并且看起来它也具有很好的延迟加载;
缺点:效率很低,实际99%状况下不须要同步。
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
写法评价:
优势:基于classloder 机制避免了多线程的同步问题,线程安全;
缺点:初始化 instance 显然没有达到延迟加载的效果。缘由是致使类装载的状况有不少种,在单例模式中咱们指望的是调用 getInstance 方法初始化 instance, 可是咱们不能肯定有其余的方式也致使类的装载如调用其余的静态方法。
public class Singleton { private Singleton instance = null; static { instance = new Singleton(); } private Singleton (){} public static Singleton getInstance() { return this.instance; } }
写法评价:
优势:使用静态代码块初始化实例,线程安全;
缺点:加载时机其实和第三种方式差很少,都是在类初始化即实例化instance, 效率比较低。
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
写法评价:
线程安全,利用了classloder的机制来保证初始化instance时只有一个线程;延迟加载,只有显示经过调用 getInstance 方法时,才会显示装载 SingletonHolder 类,再回初始化 Singleton 类。(第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化)
public enum Singleton { INSTANCE; public void whateverMethod() { } }
写法评价:
这种方式是 Effective Java 做者 Josh Bloch 提倡的方式,它不只能避免多线程同步问题,并且还能防止反序列化从新建立新的对象,可谓是很坚强的壁垒啊,不过,我的认为因为1.5中才加入 enum 特性,用这种方式写难免让人感受生疏,在实际工做中,我也不多看见有人这么写过。
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
写法评价:
使用 volatile 保证 instance 在线程间的可见性,在调用 getInstance 实例化时,使用 synchronized 锁住类模板,保证线程安全,在 synchronized 代码块中使用双重校验机制保证类只会被实例化一次,达到线程安全和延迟加载的特性。