单例模式是23种设计模式中较为简单的设计模式,面试中也是经常被面试官问到。但要真正在实际开发中运用好单例设计模式并不简单。本文主要介绍5种单例:饿汉式,懒汉式,懒汉式(内部静态类),注册式(枚举单例,threadlocal型单例(容器式))。前两种只要知道单例模式的人都知道,应付面试能讲出后三种绝对可以给自己加分。
本文不提供测试结果!
饿汉式 /** * @description:饿汉式 */ public class HungrySingleton { /** * final修饰,防止暴力反射破环单例 * * 缺点:由于使用static修饰,不管单列是否被调用,对象在初始化时就会被创建,浪费空间 */ private static final HungrySingleton hungrySingleton = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getHungrySingleton() { return hungrySingleton; } /** * readResolve方法,防止序列化破坏单列 * readResolve方法在反序列化创建对象时会用到 */ private Object readResolve(){ return hungrySingleton; } }懒汉式 ** * @description:懒汉式,双重检验锁,保证多线程下的线程安全 * * 双重检验锁,两次判断(lazySingleton == null),避免多线程时出现线程安全问题 */ public class LazySingleton { private static LazySingleton lazySingleton = null; private LazySingleton() {} public static LazySingleton getInstance() { if (lazySingleton == null) { synchronized(LazySingleton.class) { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } } } return lazySingleton; } } 懒汉式,内部静态类 /** * @description:懒汉式,内部静态类 * * 内部类LazyHolder中的逻辑 : *【private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton()】 * 需要等到外部方法getInstance()被调用时才会执行,并不会因为static修饰在初始化时就加载。 * 运用JVM底层运行逻辑,巧妙解决了线程安全问题 * 代码中没有使用synchronized修饰,性能最优 */ public class LazyInnerClassSingleton { private LazyInnerClassSingleton() { /** * 防止暴力反射,破坏单列 */ if (LazyHolder.LAZY != null){ throw new RuntimeException("LazyHolder.LAZY 已被初始化"); } } public static final LazyInnerClassSingleton getInstance() { return LazyHolder.LAZY; } private static class LazyHolder { private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton(); } } 注册式:枚举单例 ** * @description:枚举单例,注册试(枚举) * * 不存在序列化和反射破坏单例的情况 * JDK底层做了处理 */ public enum EnumSingleton { INSTANCE; private Object data; public Object getData() { return data; } public void setData(Object data) { this.data = data; } public static EnumSingleton getInstance() { return INSTANCE; } }
注册式:threadlocal型单例(容器式) /** * @description:threadlocal型单例,注册试单例(容器试) * 伪线程安全,即在同一个线程中安全,多个线程调用getInstance()得到的实例不同 * 可动态实现多数据源切换 */ public class ThreadLocalSingleton { public ThreadLocalSingleton() {} public static final ThreadLocal<ThreadLocalSingleton> threadLocal = ThreadLocal.withInitial(() -> new ThreadLocalSingleton()); public static ThreadLocalSingleton getInstance() { return threadLocal.get(); } }
