java23种设计模式-单例模式

news/2025/2/25 6:41:40

单例模式(Singleton Pattern)学习笔记

🌟 定义

单例模式属于创建型设计模式,确保一个类只有一个实例,并提供全局访问点。是Java中最简单但实现最复杂的设计模式


🎯 适用场景

  1. 需要控制资源访问(如数据库连接池)
  2. 全局配置对象
  3. 日志记录器
  4. 设备管理器(如打印机服务)
  5. 缓存系统
  6. 线程池/连接池管理

🔧 模式结构

📐 类图

Singleton
-static instance: Singleton
-Singleton()
+static getInstance()
+otherMethod()

🛠️ 核心组成

  1. 私有静态实例:存储唯一实例
  2. 私有构造方法:防止外部实例化
  3. 静态获取方法:全局访问入口
  4. 线程安全措施:保证多线程环境正确性

📝 代码实现(6种经典写法)

1. 饿汉式(线程安全)

public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {
        // 防止反射攻击
        if (INSTANCE != null) {
            throw new IllegalStateException("Already initialized");
        }
    }
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

2. 懒汉式(非线程安全)

public class LazySingleton {
    private static LazySingleton instance;
    
    private LazySingleton() {}
    
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

3. 同步方法懒汉式(线程安全)

public class SynchronizedSingleton {
    private static SynchronizedSingleton instance;
    
    private SynchronizedSingleton() {}
    
    public static synchronized SynchronizedSingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedSingleton();
        }
        return instance;
    }
}

4. 双重检查锁(DCL,线程安全)

public class DCLSingleton {
    private volatile static DCLSingleton instance;
    
    private DCLSingleton() {}
    
    public static DCLSingleton getInstance() {
        if (instance == null) {
            synchronized (DCLSingleton.class) {
                if (instance == null) {
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}

5. 静态内部类(线程安全)

public class InnerClassSingleton {
    private InnerClassSingleton() {}
    
    private static class SingletonHolder {
        static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }
    
    public static InnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

6. 枚举式(最佳实践,线程安全)

public enum EnumSingleton {
    INSTANCE;
    
    public void doSomething() {
        // 业务方法
    }
}

✅ 优点

  1. 严格控实例数量
  2. 全局唯一访问点
  3. 延迟初始化(部分实现)
  4. 减少内存开销(避免重复创建)
  5. 避免资源冲突

⚠️ 缺点

  1. 违反单一职责原则(同时控制创建和业务逻辑)
  2. 单元测试困难(全局状态难以隔离)
  3. 可能隐藏类间耦合关系
  4. 需要特殊处理序列化/反射攻击
  5. 长期持有可能造成内存泄漏

🔒 防御措施

1. 防止反射攻击

private Singleton() {
    if (instance != null) {
        throw new IllegalStateException("Already initialized");
    }
}

2. 防止序列化破坏

// 添加readResolve方法
protected Object readResolve() {
    return getInstance();
}

3. 防止克隆破坏

@Override
protected Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
}

🔄 实现对比

实现方式线程安全懒加载防止反射防止序列化性能
饿汉式⭐⭐⭐⭐
同步方法
双重检查锁⭐⭐⭐
静态内部类⭐⭐⭐⭐
枚举式⭐⭐⭐⭐

💡 实践建议

  1. 优先选择枚举实现(Effective Java推荐)
  2. 需要懒加载时用静态内部类方式
  3. 避免使用synchronized方法(性能差)
  4. 谨慎使用单例模式(考虑依赖注入)
  5. 注意与Spring单例的区别:

🚀 典型应用

  1. Runtime类(JDK内置单例)

    Runtime runtime = Runtime.getRuntime();
    
  2. 日志框架(LogManager)

    Logger logger = LogManager.getLogManager().getLogger("");
    
  3. Spring容器(ApplicationContext)

    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    

📌 扩展知识

1. 如何实现线程安全的延迟加载?

使用Initialization-on-demand holder惯用法(静态内部类实现)

2. volatile关键字的作用?

  • 保证可见性
  • 禁止指令重排序
  • 在DCL模式中防止返回未初始化完毕的对象

3. 为什么推荐枚举实现?

  • 自动处理序列化
  • 防止反射攻击
  • 保证线程安全
  • 代码简洁

掌握单例模式的关键在于理解实例控制线程安全的平衡,根据具体场景选择合适的实现方式。在分布式系统、类加载器不同的环境等特殊场景下需要特别注意单例的有效范围。


http://www.niftyadmin.cn/n/5865103.html

相关文章

如何将mobaxterm的默认编辑器修改为vscode

1.找到vscode的文件位置,复制一下你的文件路径 我这里是: C:\Users\lqj\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Visual Studio Code 2.打开mobaxterm,修改默认编辑器 按要求修改目录即可 3.测试: 双击文件再…

python-leetcode 42.验证二叉搜索树

题目: 给定二叉树的根节点root,判断是否是一个有效二叉搜索树 有效二叉搜索树: 1.节点的左子树只包含小于当前节点的树 2.节点的右子树只包含大于当前节点的树 3.所有左子树和右子树自身必须也是二叉搜索树 方法一:递归 如果该二叉树的…

【Linux】初识进程概念与 fork 函数的应用

Linux相关知识点可以通过点击以下链接进行学习一起加油!初识指令指令进阶权限管理yum包管理与vim编辑器GCC/G编译器make与Makefile自动化构建GDB调试器与Git版本控制工具Linux下进度条冯诺依曼体系与计算机系统架构 进程是操作系统中资源分配和调度的核心单位&#…

Junit+Mock

base project <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.11</version><relativePath/></parent><dependencies><!--添加mysql依…

微信小程序调用火山方舟(字节跳动火山引擎)中的DeepSeek大模型

一、注册火山引擎账号&#xff0c;创建API Key和model&#xff08;接入点ID&#xff09; 1.注册并登陆火山引擎账号&#xff0c;网址为&#xff1a;https://console.volcengine.com/ 2.根据登陆后的页面提示进行实名认证&#xff0c;实名认证后才能创建API Keyt和创建接入点。…

码率和采样率

“视频叫码率&#xff0c;音频叫采样率”这两者的命名来自于它们在处理过程中所关注的不同方面和技术原理&#xff0c;具体的解释如下&#xff1a; 1. 视频&#xff1a;码率 (Bitrate) 视频的码率指的是在视频编码过程中&#xff0c;每秒钟传输的数据量&#xff0c;通…

Oracle 数据泵迁移步骤规范

1、调研模块 1.1、确认迁移用户 以全库迁移为标准&#xff0c;也可直接通过需求方获取需要迁移的用户 1&#xff09;确认数据库中所有用户及其创建时间 alter session set nls_date_formatyyyy-mm-dd-hh24:mi:ss; select username,created from dba_users order by 2; 2&a…

docker compose安装redis

一、安装准备 在docker hub查看redis镜像版本。查看地址如下&#xff1a; Docker[这里是图片001]https://hub-stage.docker.com/_/redis/tags 二、拉取docker镜像 我这里用redis:6.2.14版本&#xff0c;先拉取镜像。命令如下&#xff1a; docker pull redis:6.2.14查看刚刚…