介绍单例模式

描述

保证一个类只有一个实例,并且提供一个全局访问点

场景:

重量级的对象,不需要多个实例,如线程池,数据库连接池

实现

1. 懒汉模式
  • 延迟加载的方式 只有在真正使用的时候,才开始实例化
  • 线程安全问题
  • double check 加锁优化
  • 编译器(JIT) cpu有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字,对于volatile修饰的字段,可以防止指令重排
class LazySingleton{
    private volatile static LazySingleton instance;
    private LazySingleton(){}
    public static LazySingleton getInstance(){
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();// 1.分配空间 2.初始化 3.引用赋值
                }
            }
        }
        return instance;
    }
}

备注:
javap -v XXX.class可以看class文件的字节码

2. 饿汉模式
  • 类加载的初始化阶段就完成了实例的初始化,本质上是基于JVM类加载机制,保证实例的唯一性
  • 类加载的过程:
    • 加载二进制数据到内存中,生成对应的class数据结构
    • 连接:验证、准备(给类的静态成员变量赋默认值)、解析
    • 初始化:给类的静态变量赋值
      注意:
    • 只有在真正使用对应的类时,才会触发初始化
class HungrySingleton{
    private static final long serialVersionUID = 4416608876659526091L;
    private static HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance(){
        return instance;
    }
}
3. 静态内部类
  • 本质上是利用类的加载机制保证线程安全
  • 只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){}
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}
4. 反射攻击实例
public class HungrySingletonTest {
    public static void main(String[] args) throws Exception {
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance1 = HungrySingleton.getInstance();
        System.out.println(instance);
        System.out.println(instance1);

//        // 反射获取实例
        Constructor<HungrySingleton> declaredConstructor = HungrySingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        HungrySingleton instance2 = declaredConstructor.newInstance();
        System.out.println(instance2);  
    } 
}

class HungrySingleton{ 
    private static HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){
        if (instance != null) {
            throw new RuntimeException("单例不允许创建多个实例!");
        }
    }
    public static HungrySingleton getInstance(){
        return instance;
    } 
}
5. 枚举

枚举类型支持反序列化的操作 并且不能用反射攻击
其他类型支持反序列化操作案例

public class HungrySingletonTest {
    public static void main(String[] args) throws Exception {
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance1 = HungrySingleton.getInstance();
        System.out.println(instance);
        System.out.println(instance1);

//        // 反射获取实例
//        Constructor<HungrySingleton> declaredConstructor = HungrySingleton.class.getDeclaredConstructor();
//        declaredConstructor.setAccessible(true);
//        HungrySingleton instance2 = declaredConstructor.newInstance();
//        System.out.println(instance2);

        // 序列化
        HungrySingleton instance2 = HungrySingleton.getInstance();
//        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("testSerializable"));
//        oss.writeObject(instance2);
//        oss.close();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable"));
        HungrySingleton o = (HungrySingleton) ois.readObject();
        ois.close();
        System.out.println(o == instance2);

    }

}

class HungrySingleton implements Serializable{
    private static final long serialVersionUID = 4416608876659526091L;
    private static HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){
        if (instance != null) {
            throw new RuntimeException("单例不允许创建多个实例!");
        }
    }
    public static HungrySingleton getInstance(){
        return instance;
    }

    public Object readResolve() throws ObjectStreamException {
        return getInstance();
    }
}

相关推荐

  1. 模式介绍

    2024-06-07 09:44:04       13 阅读
  2. 介绍模式

    2024-06-07 09:44:04       9 阅读
  3. 模式介绍

    2024-06-07 09:44:04       30 阅读
  4. 模式模板

    2024-06-07 09:44:04       19 阅读
  5. 模式【C#】

    2024-06-07 09:44:04       37 阅读
  6. python模式

    2024-06-07 09:44:04       34 阅读
  7. 模式详解

    2024-06-07 09:44:04       39 阅读
  8. 模式学习

    2024-06-07 09:44:04       28 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-07 09:44:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-07 09:44:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-07 09:44:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-07 09:44:04       18 阅读

热门阅读

  1. Jitsi meet 退出房间后,用户还在房间内

    2024-06-07 09:44:04       9 阅读
  2. lua vm 四: 表达式

    2024-06-07 09:44:04       7 阅读
  3. 获取gitee上某个组织所有仓库的介绍

    2024-06-07 09:44:04       7 阅读
  4. 系统研发安全漏洞

    2024-06-07 09:44:04       6 阅读
  5. vue el-dialog封装成子组件(组件化)

    2024-06-07 09:44:04       7 阅读
  6. react-intl国际化在项目中的使用

    2024-06-07 09:44:04       6 阅读
  7. 浅谈人机交互

    2024-06-07 09:44:04       5 阅读
  8. 人机交互中的阴差阳错

    2024-06-07 09:44:04       4 阅读