创建型模式-单例模式

1.定义

单例模式是一种创建型设计模式,它确保在jvm内存中,一个类的实例只有一个,并且对外提供一个可以访问它的静态方法。核心目的是做资源控制,如数据库连接池,全局配置类等,限制只有一个实例,避免资源浪费和导致资源冲突。

2.实现
  • 饿汉式(最简单,最常用) :类加载到 JVM 时就创建好实例。优点 :绝对线程安全(类加载机制保证),没有锁,响应快。缺点:不管用不用,启动就占内存。
  • 懒汉式(双检锁 DCL) :需要时才创建。使用volatile + synchronized 双重检查。
  • 静态内部类(最优雅) :利用 JVM 类加载的懒加载机制。既能实现懒加载,又能保证线程安全,代码比 DCL 简洁。
3.应用场景
  • 日志记录器:全系统使用同一个日志上下文实例,保证日志输出的格式、存储路径和异步刷盘策略统一。
  • 配置管理:全局配置读取。
  • 池化管理:数据库连接池,线程池。
4.优缺点

优点:

  • 可以保证一个类只有一个实例。
  • 获得了一个指向该实例的全局访问节点。

缺点:

  • 违反了单一职责原则。该模式同时解决了两个问题。
  • 单例模式可能掩盖不良设计, 比如程序各组件之间相互了解过多等。
  • 该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。
5.示例代码

饿汉式

java 复制代码
public class SingletonEager {

    // ★ 核心点1:类加载时即完成初始化,由JVM保证线程绝对安全
    // 无需 volatile,因为 JVM 在类加载的初始化阶段(<clinit>() 方法)会保证静态变量的赋值顺序
    // 不存在并发争抢,因此也不存在指令重排带来的可见性问题
    private static final SingletonEager instance = new SingletonEager();

    // 私有构造器,防止外部 new
    private SingletonEager() {
        // 防反射攻击(饿汉式同样适用)
        // 由于类加载时 instance 已经不为 null,反射调用构造器时会被拦截
        if (instance != null) {
            throw new RuntimeException("请勿通过反射破坏单例");
        }
    }

    public static SingletonEager getInstance() {
        // ★ 核心点2:直接返回,无需任何同步检查或判空逻辑
        // 调用时毫无性能开销,但若从未使用,资源依然被加载(类加载时即占用内存)
        return instance;
    }
}

懒汉式(双重检查锁)

java 复制代码
public class SingletonDCL {

    // ★ 核心点1:必须加 volatile,防止指令重排序
    // 防止 instance = new SingletonDCL() 时,赋值操作先于对象初始化完成,导致其他线程拿到半成品对象
    private static volatile SingletonDCL instance;

    // 私有构造器,防止外部 new
    private SingletonDCL() {
        // 防反射攻击(可选,但加分项)
        if (instance != null) {
            throw new RuntimeException("请勿通过反射破坏单例");
        }
    }

    public static SingletonDCL getInstance() {
        // 第一重检查:避免不必要的同步(性能优化)
        if (instance == null) {
            // ★ 核心点2:类锁,保证线程安全
            synchronized (SingletonDCL.class) {
                // 第二重检查:防止在等待锁的过程中,被其他线程抢先创建了实例
                if (instance == null) {
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

静态内部类:

java 复制代码
public class SingletonHolder {

    // 私有构造器
    private SingletonHolder() {
        // 防反射攻击
        if (Holder.INSTANCE != null) {
            throw new RuntimeException("请勿通过反射破坏单例");
        }
    }

    // ★ 核心点:利用 JVM 类加载机制保证线程安全,且实现懒加载
    // 只有调用 getInstance() 时,Holder 类才会被加载,INSTANCE 才会初始化
    private static class Holder {
        private static final SingletonHolder INSTANCE = new SingletonHolder();
    }

    public static SingletonHolder getInstance() {
        return Holder.INSTANCE;
    }
}
相关推荐
金銀銅鐵13 小时前
为什么枚举类中可以定义抽象方法
java·后端·ai编程
文丑颜不良啊15 小时前
LeetCode1941~1960题目解析
java·leetcode
txxzjmzlh15 小时前
SpringBoot 统⼀功能处理
java·spring boot·后端
nice_lcj52017 小时前
Java Lambda 表达式入门:怎么写、怎么用、常用方法有哪些
java·开发语言·后端·面试
码农小河6617 小时前
基于SSM+JSP+MySQL的在线医疗服务系统的设计与实现
java·开发语言·mysql
SL_staff18 小时前
从配置失效到逻辑复用:JVS-Logic 如何构建可治理的业务编排中枢
java·spring·产品
特立独行的猫A19 小时前
一份 Markdown 发十个平台:cspub 一稿多投
java·后端·python
文丑颜不良啊19 小时前
LeetCode1961~1980题目解析
java·leetcode
文丑颜不良啊20 小时前
LeetCode2001~2020题目解析
java·leetcode
用户37215742613520 小时前
Java 批量提取 Word 文档中的图片并保存到本地
java