单例模式
- 描述
- 基本使用
- 防破坏单例
- 饿汉式
- 懒汉式
- 有上限多例
描述
一个类全局只有一个实例,自行实例化并提供给使用。
构造函数私有化是前提
基本使用
防破坏单例
- 防反射:在构造函数中判断类中实例是否已初始化
private InnerClassSingleton (){if(InnerClassSingletonHolder.instance != null){throw new RuntimeException("单例模式 不允许 多例存在");}
}
- 防克隆:单例类不要实现Cloneable接口或者强制覆盖clone方法
如果采用的是强制覆盖clone方法,可以返回单例对象或者 直接报错 - 防序列化:对象反序列化会调用对象的readResolve方法,可以在类中声明这个方法,并返回单例对象
Object readResolve() throws ObjectStreamException{return InnerClassSingletonHolder.instance;}
饿汉式
- 静态成员初始化
public class HungrySingleton {private static HungrySingleton instange = new HungrySingleton();private HungrySingleton() {if (instange != null) {throw new RuntimeException("单例模式不允许 多例存在");}}public static HungrySingleton getInstance() {return instange;}
}
- 静态代码块初始化
public class HungrySingleton {private static HungrySingleton instange;static {instange = new HungrySingleton();}private HungrySingleton() {if (instange != null) {throw new RuntimeException("单例模式不允许 多例存在");}}public static HungrySingleton getInstance() {return instange;}
}
- 枚举(直接用吧JVM层面保证单例)
/*** @author sdaf* <p>* 枚举:本质也是一个类,底层是一个实现 Eunm 的一个类,编译器会默认添加一些属性及方法* 枚举站在单例实现角度上看 就是一个饿汉模式实现*/
public enum EnumSingleton {INSTANGE;public void test(){// code}
}
懒汉式
- synchronized + double check
public class LazySingleton {private static volatile LazySingleton lazySingleton;private LazySingleton() {if(lazySingleton!=null){throw new RuntimeException("单例模式不允许 多例存在");}}public static LazySingleton getInstance() {if (lazySingleton == null) {// 线程A 线程B 均进入非空判断逻辑// 线程A 先行添加锁,然后创建实例对象,并返回// 此时线程B已经 进入非空的逻辑,然后依然会去加锁,然后创建对象,此时就违背了单例对象定义// 所以需要在加锁的内部再次进行非空判断,保证对象单例synchronized (LazySingleton.class) {if (lazySingleton == null) {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}lazySingleton = new LazySingleton();// new 一个对象步骤进一步解析// 1、在堆内存开辟一块空间// 2、初始化对象(创建对象)// 3、将开辟的内存空间地址赋值给变量// 【但是】由于编译器和CPU的指令重排可能导致 2 和 3 执行顺序发生改变// 为 1 3 2// 此时如果第二个线程 在 第一个线程执行到 3 与 2之间时 进来// instance 是有值的,因为 instance 拿到的只是 内存地址而已,并不知道这个地址上是否实在的对象// 只有当这个对象完成初始化(创建)后这个地址上才有具体的值// 此时第二个线程拿到地址就会直接返回这个引用,// 但是此时这个对象是个 null , 可能会引起 NullPointerException// 为了防止 整个 new 操作被执行重排 需要使用关键字 volatile 来修饰变量}}}return lazySingleton;}
- 静态内部类
/*** @author sdaf* @Description 基于静态内部类的单例模式,也是一种懒加载* 也是借助于JVM类加载机制实现的线程安全* 类加载顺序:* 1.加载类的Class文件到内存中* 2.连接JVM,验证Class合法性,静态成员赋默认值,解析处理后的数据* 3.给静态成员赋值* 静态内部类只有在使用这个类的时候才会去加载这个类*/
public class InnerClassSingleton implements Serializable{private static final long serialVersionUID = -790330830636259886L;private static class InnerClassSingletonHolder{private static InnerClassSingleton instance= new InnerClassSingleton();}private InnerClassSingleton (){if(InnerClassSingletonHolder.instance != null){throw new RuntimeException("单例模式 不允许 多例存在");}}public static InnerClassSingleton getInstance(){return InnerClassSingletonHolder.instance;}
//Object readResolve() throws ObjectStreamException{return InnerClassSingletonHolder.instance;}
}
有上限多例
单例模式可以扩展为有上限多例
public class Multiton {private Multiton() {}private static final int MAX_INSTANCES = 3;private static final List<Multiton> instances = new ArrayList<>();// 饿汉式初始化static {for (int i = 0; i < MAX_INSTANCES; i++) {instances.add(new Multiton());}}// 懒汉式初始化public static Multiton getInstanceLazy(int index) {if (instances.get(index) == null) {synchronized (Multiton.class) {if (instances.get(index) == null) {instances.set(index, new Multiton());}}}return instances.get(index);}// 随机获取public static Multiton getRandomInstance() {Random random = new Random();return instances.get(random.nextInt(MAX_INSTANCES));}// 指定获取public static Multiton getInstance(int index) {if (index < 0 || index >= MAX_INSTANCES) {throw new IllegalArgumentException("索引超出范围");}return instances.get(index);}// 业务方法public void test() {}
}