第 1 天:单例模式(Singleton Pattern)—— 创建型模式

第 1 天:单例模式(Singleton Pattern)------ 创建型模式

1. 核心定义

单例模式确保一个类仅有一个实例 ,并提供一个全局访问点来获取该实例。它的核心是 "控制实例数量",避免重复创建消耗资源的对象(如数据库连接池、配置管理器)。

2. 解决的核心问题

  • 避免多个实例导致的资源浪费(如频繁创建数据库连接);
  • 防止多个实例引发的数据不一致(如配置文件读取实例,多实例可能导致配置冲突);
  • 提供统一的全局访问入口,简化对象调用。

3. 关键设计要点

  • 私有构造函数 :禁止外部通过 new 关键字创建实例;
  • 私有静态成员变量:存储类的唯一实例;
  • 公有静态方法:作为全局访问点,返回唯一实例(需处理线程安全问题)。

4. 常见实现方式(Java 示例)

方式 1:饿汉式(线程安全,简单但可能浪费资源)

"饿汉式" 在类加载时就创建实例,避免线程安全问题,但如果实例未被使用,会提前占用内存。

csharp 复制代码
public class HungrySingleton {
    // 1. 私有静态成员变量:类加载时创建唯一实例
    private static final HungrySingleton INSTANCE = new HungrySingleton();
    
    // 2. 私有构造函数:禁止外部new
    private HungrySingleton() {}
    
    // 3. 公有静态方法:返回实例
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

方式 2:懒汉式(线程不安全→线程安全优化)

"懒汉式" 在首次调用 getInstance() 时才创建实例,节省内存,但需额外处理线程安全(否则多线程下可能创建多个实例)。

线程安全优化版(双重检查锁定)

csharp 复制代码
public class LazySingleton {
    // 1. 私有静态成员变量:volatile防止指令重排序(关键!)
    private static volatile LazySingleton INSTANCE;
    
    // 2. 私有构造函数
    private LazySingleton() {}
    
    // 3. 公有静态方法:双重检查锁定
    public static LazySingleton getInstance() {
        // 第一次检查:避免已创建实例时的锁竞争
        if (INSTANCE == null) {
            synchronized (LazySingleton.class) {
                // 第二次检查:防止多线程同时进入第一个检查后重复创建
                if (INSTANCE == null) {
                    INSTANCE = new LazySingleton();
                }
            }
        }
        return INSTANCE;
    }
}

方式 3:静态内部类(推荐,线程安全且延迟加载)

利用 Java "静态内部类加载时机" 的特性:内部类在被调用时才加载,既实现延迟加载,又天然避免线程安全问题(类加载过程由 JVM 保证线程安全)。

csharp 复制代码
public class StaticInnerClassSingleton {
    // 1. 私有构造函数
    private StaticInnerClassSingleton() {}
    
    // 2. 静态内部类:存储唯一实例
    private static class SingletonHolder {
        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }
    
    // 3. 公有静态方法:调用内部类的实例
    public static StaticInnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

5. 应用场景

  • 系统中只能有一个实例的对象,如:

    • 数据库连接池(DataSource);
    • 配置文件管理器(读取全局配置);
    • 日志工具类(避免多实例写入日志时的冲突);
    • 操作系统的任务管理器、回收站。

6. 注意事项

  • 线程安全:懒汉式必须处理线程安全,否则多线程环境下会破坏 "单例" 特性;
  • 序列化问题 :若单例类实现 Serializable,需重写 readResolve() 方法,否则反序列化会创建新实例;
  • 反射攻击:私有构造函数可通过反射被调用,需在构造函数中添加判断(若实例已存在则抛异常)。
相关推荐
我不是混子2 小时前
什么是内存泄漏?
java
程序员小假2 小时前
我们来说说当一个线程两次调用 start() 方法会出现什么情况?
java·后端
SimonKing3 小时前
Archery:开源、一站式的数据库 SQL 审核与运维平台
java·后端·程序员
皮皮林55114 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
卡尔特斯18 小时前
Android Kotlin 项目代理配置【详细步骤(可选)】
android·java·kotlin
白鲸开源18 小时前
Ubuntu 22 下 DolphinScheduler 3.x 伪集群部署实录
java·ubuntu·开源
ytadpole18 小时前
Java 25 新特性 更简洁、更高效、更现代
java·后端
纪莫19 小时前
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
java·java面试⑧股
JavaGuide20 小时前
JDK 25(长期支持版) 发布,新特性解读!
java·后端