人生有涯,学海无涯
一、概述
单例模式(Singleton Pattern)是 Java 中最简略的设计模式之一。这种类型的设计模式属于创立型模式,它提供了一种创建对象的最佳形式。
这种模式波及到一个繁多的类,该类负责创立本人的对象,同时确保只有单个对象被创立。这个类提供了一种拜访其惟一的对象的形式,能够间接拜访,不须要实例化该类的对象。
留神:
- 1、单例类只能有一个实例。
- 2、单例类必须本人创立本人的惟一实例。
- 3、单例类必须给所有其余对象提供这一实例。
二、七种实现
2.1、懒汉式 — 线程不平安
是否 Lazy 初始化:是是否多线程平安:否
实现难度:易
形容:这种形式是最根本的实现形式,这种实现最大的问题就是不反对多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种形式 lazy loading 很显著,不要求线程平安,在多线程不能失常工作。
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
2.2、懒汉式 — 线程平安
是否 Lazy 初始化:是是否多线程平安:是
实现难度:易
形容:这种形式具备很好的 lazy loading,可能在多线程中很好的工作,然而,效率很低,99% 状况下不须要同步。
长处:第一次调用才初始化,防止内存节约。
毛病:必须加锁 synchronized 能力保障单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很要害(该办法应用不太频繁)。
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
2.3、饿汉式
是否 Lazy 初始化:否是否多线程平安:是
实现难度:易
形容:这种形式比拟罕用,但容易产生垃圾对象。
长处:没有加锁,执行效率会进步。
毛病:类加载时就初始化,节约内存。
它基于 classloader 机制防止了多线程的同步问题,不过,instance 在类装载时就实例化,尽管导致类装载的起因有很多种,在单例模式中大多数都是调用 getInstance 办法, 然而也不能确定有其余的形式(或者其余的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的成果。
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
2.4、饿汉式 — 变种
其实和之前的一样,仅仅是将单例的创立挪到了动态块。
public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton (){} public static Singleton getInstance() { return instance; } }
2.5、枚举式
JDK 版本:JDK1.5 起是否 Lazy 初始化:否
是否多线程平安:是
实现难度:易
形容:这种实现形式还没有被宽泛采纳,但这是实现单例模式的最佳办法。它更简洁,主动反对序列化机制,相对避免屡次实例化。
这种形式是 Effective Java 作者 Josh Bloch 提倡的形式,它不仅能防止多线程同步问题,而且还主动反对序列化机制,避免反序列化从新创立新的对象,相对避免屡次实例化。不过,因为 JDK1.5 之后才退出 enum 个性,用这种形式写未免让人感觉陌生,在理论工作中,也很少用。
不能通过 reflection attack 来调用公有构造方法。
public enum Singleton { INSTANCE; }
2.6、双重校验锁式
JDK 版本:JDK1.5 起是否 Lazy 初始化:是
是否多线程平安:是
实现难度:较简单
形容:这种形式采纳双锁机制,平安且在多线程状况下能放弃高性能。
getInstance() 的性能对应用程序很要害。
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的应用,为了避免裸露一个未初始化的不残缺单例实例;
- 双重判空校验,第一个判断防止了频繁的加锁,第二个判断能够拦住多余的创立实例的线程;
- 加锁,保障了线程平安(只有一个实例)
这种实现形式是经常出现在面试题中的,而且常常会要求手写。
2.7、动态外部类
是否 Lazy 初始化:是是否多线程平安:是
实现难度:个别
形容:这种形式能达到双检锁形式一样的效用,但实现更简略。对动态域应用提早初始化,应应用这种形式而不是双检锁形式。这种形式只实用于动态域的状况,双检锁形式可在实例域须要提早初始化时应用。
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
三、总结
下面列举的7种设计模式中第1种线程不平安,能够排除在外,第3、4种其实是一种,这样下来其实能够简化为5种形式:懒汉、饿汉、动态外部类、枚举、双重校验锁。