单例模式(Singleton Pattern)是创建型设计模式中最简单且应用最为广泛的设计模式之一,单例模式属于创建型模式,提供了一种对象创建的思路。
使用单例模式时,目标类被要求确保有且仅有一个实例,对于系统任一对目标类的访问,不需要单独实例化类,只需要访问由目标类负责创建的唯一对象实例即可。
出现以下场景,可以考虑使用单例模式
使用单例模式的优点:
单例模式的不足:
顾名思义,此种方式实现的单例模式是在目标类要被使用时才创建实例,这种懒加载的好处是在目标类未被实际使用时,不会有实例占据内存空间。
此种实现方式线程不安全,在目标类实例还未被创建时,如果有两个线程同时访问了getInstance(),容易产生多个实例,破坏实例的唯一性,不推荐使用。
public class Singleton { public static Singleton instance; // 使用私有构造方法以避免被外部实例化 确保单例 private Singleton(){} public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
针对前一种线程不安全的实现方式,可以使用synchronized关键字修饰方法,确保方法同时只能被一个线程访问。虽然此种方式可以保证线程安全同时实现单例,但在单例被创建后,synchronized便失去了本场景下的作用(确保实例的唯一性),且会降低性能开销
public class Singleton { public static Singleton instance; // 使用私有构造方法以避免被外部实例化 确保单例 private Singleton(){} //使用synchronized保证线程安全 public synchronized static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
前文提及懒汉式为保证线程安全引入synchronized导致性能开销提升,由此衍生出了双重检查锁定(Double Check Lock)的单例实现方式,使用volatile关键字保证可见性避免JVM指令重排引发的问题,同时使用synchronized保证实例创建过程唯一,既保证了懒加载的线程安全,又相较synchronized修饰方法减少了性能开销。
public class DCLSingleton { // 注意instance必须要由volatile关键字修饰 目的是保证instance变量的可见性 避免由于指令重排破坏单例的唯一性 private static volatile DCLSingleton instance; private DCLSingleton() {} public static DCLSingleton getInstance() { if(instance == null) { //首先判断是否实例化 //使用synchronized加锁保证实例创建唯一 synchronized (DCLSingleton.class) { if(instance == null) { instance = new DCLSingleton(); } } } return instance; } }
饿汉式单例的思路是利用static关键字,使目标类在类加载阶段就创建唯一实例,保证实例在被访问前就已经被创建,确保唯一性,线程安全。
public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
public class Singleton { private static Singleton instance; private Singleton(){} static{ instance = new Singleton(); } public static Singleton getInstance(){ return instance; } }
在外部类被装载时,静态内部类不会跟着一起被装载,而是在静态内部类实际被使用的时候(getInstance被调用)才会装载并实例化,所以使用静态内部类实现单例模式具有懒加载的优点,同时JVM在装载类的过程中可以保证线程安全。
public class SingletonStaticClass { private SingletonStaticClass(){} public static final SingletonStaticClass getInstance(){ return SingletonInstance.instance; } private static class SingletonInstance{ private static final SingletonStaticClass instance = new SingletonStaticClass(); } }
枚举实现单例是最安全且写法最简单的,JVM会保证枚举类不能被反射且构造器只能被调用一次,彻底解决了其他单例模式容易被反射和序列化反序列化攻击的问题(见下文),推荐使用。
public enum EnumSingleton { INSTANCE; public void method() { System.out.println("doSomething"); } }
public class EnumSingletonEnhance { private EnumSingletonEnhance(){} public enum SingletonEnum { SINGLETON_ENUM; private EnumSingletonEnhance instance = null; private SingletonEnum(){ instance = new EnumSingletonEnhance(); } public EnumSingletonEnhance getinstance(){ return instance; } } }