设计模式-单例模式
单例模式是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();
}
}