设计模式简述(二)单例模式

单例模式

描述

一个类全局只有一个实例,自行实例化并提供给使用。

构造函数私有化是前提

基本使用

防破坏单例

  • 防反射:在构造函数 中判断类中实例是否已初始化
java 复制代码
private InnerClassSingleton (){
    if(InnerClassSingletonHolder.instance != null){
        throw new RuntimeException("单例模式 不允许 多例存在");
    }
}
  • 防克隆:单例类不要实现Cloneable接口或者强制覆盖clone方法
    如果采用的是强制覆盖clone方法,可以返回单例对象或者 直接报错
  • 防序列化:对象反序列化会调用对象的readResolve方法,可以在类中声明这个方法,并返回单例对象
java 复制代码
    Object readResolve() throws ObjectStreamException{
        return InnerClassSingletonHolder.instance;
    }

饿汉式

  • 静态成员初始化
java 复制代码
public class HungrySingleton {

    private static HungrySingleton instange = new HungrySingleton();

    private HungrySingleton() {
        if (instange != null) {
            throw new RuntimeException("单例模式不允许 多例存在");
        }
    }

    public static HungrySingleton getInstance() {
        return instange;
    }
}
  • 静态代码块初始化
java 复制代码
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层面保证单例)
java 复制代码
/**
 * @author sdaf
 * <p>
 * 枚举:本质也是一个类,底层是一个实现 Eunm 的一个类,编译器会默认添加一些属性及方法
 * 枚举站在单例实现角度上看 就是一个饿汉模式实现
 */
public enum EnumSingleton {
    INSTANGE;
    public void test(){
    // code
    }
}

懒汉式

  • synchronized + double check
java 复制代码
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;
    }
  • 静态内部类
java 复制代码
/**
 * @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;
    }
}

有上限多例

单例模式可以扩展为有上限多例

java 复制代码
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() {
        
    }
}
相关推荐
碎梦归途5 小时前
23种设计模式-行为型模式之策略模式(Java版本)
java·开发语言·jvm·设计模式·策略模式·行为型模式
Java致死7 小时前
单例设计模式
java·单例模式·设计模式
麓殇⊙11 小时前
设计模式--桥接模式详解
设计模式·桥接模式
学习机器不会机器学习12 小时前
深入浅出JavaScript常见设计模式:从原理到实战(1)
开发语言·javascript·设计模式
ApeAssistant15 小时前
Spring + 设计模式 (二十) 行为型 - 中介者模式
spring·设计模式
ApeAssistant15 小时前
Spring + 设计模式 (十九) 行为型 - 访问者模式
spring·设计模式
〆、风神15 小时前
从零实现分布式WebSocket组件:设计模式深度实践指南
分布式·websocket·设计模式
前端大白话16 小时前
Vue2和Vue3语法糖差异大揭秘:一文读懂,开发不纠结!
javascript·vue.js·设计模式
前端大白话16 小时前
JavaScript中`Symbol.for()`和`Symbol()`的区别,在创建全局唯一的`Symbol`值时如何选择使用?
前端·javascript·设计模式