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

单例模式

描述

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

构造函数私有化是前提

基本使用

防破坏单例

  • 防反射:在构造函数 中判断类中实例是否已初始化
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() {
        
    }
}
相关推荐
wenbin_java3 小时前
设计模式之解释器模式:原理、实现与应用
java·设计模式·解释器模式
-权子-5 小时前
设计模式 - 代理模式Proxy
设计模式·代理模式
StrongerLLL6 小时前
策略模式
设计模式
程序员沉梦听雨7 小时前
适配器模式详解
设计模式·适配器模式
渊渟岳8 小时前
为什么学习设计模式?
设计模式
程序员JerrySUN13 小时前
设计模式 Day 3:抽象工厂模式(Abstract Factory Pattern)详解
设计模式·抽象工厂模式
程序员沉梦听雨16 小时前
原型模式详解
设计模式·原型模式
coderzpw16 小时前
谁该处理我的请假?——责任链模式
设计模式·责任链模式
云徒川18 小时前
【设计模式】组合模式
设计模式·组合模式