单例模式(Singleton) —— 对象创建型模式
本文记录单例模式从设计理念的简单到复杂,从运行效果的低效到高效,各种不同实现方式。涉及到多线程和JVM。
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
The Singleton Pattern ensures a class has only one instance, and provides a gloable point of access to it.
实现一:最简单,线程不安全。
/** * @description 最简单的实现,线程不安全。 * @author michael */ public class Singleton { private static Singleton uniqueInstance; //Other useful Singleton data... /** * 构造器私有化 */ private Singleton() { } /** * @description 抽象工程方法(static factory),获得该类唯一实例。 * @return 唯一实例 */ public static Singleton getInstance() { if(null == uniqueInstance) { uniqueInstance = new Singleton(); } return uniqueInstance; } //Other useful Singleton methods... }
实现二:同步方法
使用synchronized关键字,同步getInstance()方法,确保任意时刻只有一个线程可以访问该方法。线程安全,效率低。
/** * @description 线程安全,效率低。 * @author michael */ public class Singleton { private static Singleton uniqueSingleton; //Other useful Singleton data... /** * 构造器私有化 */ private Singleton() { } /** * @description 同步方法,线程安全,效率低。 * @return 唯一实例 */ public static synchronized Singleton getInstance() { if(uniqueSingleton == null) { uniqueSingleton = new Singleton(); } return uniqueSingleton; } //Other useful Singleton methods... }
实现三:饿汉式
先于方法调用前创建实例,静态初始化变量。依赖于JVM的实现,虚拟机要确保在任何线程访问静态变量uniqueSingleton之前创建好实例。
/** * @description move to an eagerly created instance rather than a lazily created one. * @author michael */ public class Singleton { private static Singleton uniqueSingleton = new Singleton(); //Other useful Singleton data... /** * 构造器私有化 */ private Singleton() { } /** * @description eagerly created * @return 唯一实例 */ public static synchronized Singleton getInstance() { return uniqueSingleton; } //Other useful Singleton methods... }
实现四:双检测
使用volatile关键字修饰变量,确保多线程访问修改正确。双检测,只有在第一次调用getInstance()才会同步类。 版本要求:JDK1.5或以上。
解释下为什么需要双重检测,假设此时uniqueSingleton == null,两个线程同时调用getInstance()方法,由于uniqueSingleton还未创建,
if条件满足, 两个线程都会进入if语句内部。但是下一步只有一个线程会拿到锁,进入到同步代码块,此时再次检测依然为空,创建实例,
释放锁,并返回实例。 当另一个线程获得锁进入同步代码块时,实例已创建,释放锁返回实例。这是双重检测的情况,如果不在同步块内二次检测,
同样两个线程同时访问 getInstance()方法, 在if语句内, 同步方法块外堵塞。获得锁的线程创建实例、释放锁并返回实例。当另一个线程获得锁执行同步代码时,
没有进行二次 检测,尽管此时uniqueSingleton已经被创建,后获得锁的线程依然会再创建一个实例,违反了唯一实例的原则。
/** * @description double checked * @author michael * @see 1.5 */ public class Singleton { //volatile 多线程共享变量 private volatile static Singleton uniqueSingleton; //Other useful Singleton data... /** * 构造器私有化 */ private Singleton() { } /** * @description 双检测 * @return 唯一实例 */ public static synchronized Singleton getInstance() { if(uniqueSingleton == null) { //多线程 可能在这堵塞 双检测 避免重复构造 synchronized(Singleton.class) { if(uniqueSingleton == null) { uniqueSingleton = new Singleton(); } } } return uniqueSingleton; } //Other useful Singleton methods... }
实现五:基于JVM对实现四的改进,避免引用还未完全初始化的对象。
解释:当第一次调用getInstance()方法,执行uniqueSingle = new Singleton()这句代码,JVM可能在new Single()并未完全执行,
就已经给uniqueSingleton 分配地址(uniqueSingleton != null),而此时如果另一个线程调用getInstance()方法,
就会得到还未完全初始化的uniqueSingleton的引用。实现五使用一个中间 变量singleton确保uniqueSingleton的完整性。
/** * @description double checked * @author michael * @see since 1.5 */ public class Singleton { //volatile 多线程共享变量 private volatile static Singleton uniqueSingleton; //Other useful Singleton data... /** * 构造器私有化 */ private Singleton() { } /** * @description 双检测 * @return 唯一实例 */ public static synchronized Singleton getInstance() { if(uniqueSingleton == null) { //多线程 可能在这堵塞 双检测 避免重复构造 synchronized(Singleton.class) { if(uniqueSingleton == null) { //avoid getting a reference to an incompletely initialized object Singleton singleton = new Singleton(); uniqueSingleton = singleton; } } } return uniqueSingleton; } //Other useful Singleton methods... }
1.对唯一实例的受控访问
2.缩小名空间
3.允许对操作和表示的精化
4.允许可变数目的实例
5.比类操作更灵活
很多模式可以使用单例模式实现。参见抽象工厂模式(Abstract Factory)、建造者模式(Builder),和原型模式(Prototype)。