java学习--单例模式之懒汉式

在 Java 单例模式中,懒汉式 是一种延迟初始化 的实现方式,核心特点是实例在首次被使用时才创建(而非类加载时),避免了饿汉式可能造成的资源浪费,适用于实例占用资源大或使用频率低的场景。以下是懒汉式的常见实现方式、特点及注意事项:

一、懒汉式的核心思想

  • 延迟实例化 :不随类加载创建实例,而是在第一次调用getInstance()方法时初始化。
  • 按需创建:仅当实例被实际使用时才分配资源,提高资源利用率。

二、懒汉式的实现方式

1. 基础实现(非线程安全)

最简单的懒汉式,但多线程环境下会创建多个实例,仅适用于单线程场景:

复制代码
public class LazySingleton {
    // 静态实例初始化为null,延迟初始化
    private static LazySingleton instance;

    // 私有构造器:禁止外部通过new创建实例
    private LazySingleton() {}

    // 公共方法:首次调用时创建实例
    public static LazySingleton getInstance() {
        if (instance == null) { // 未初始化时创建
            instance = new LazySingleton();
        }
        return instance;
    }
}

问题 :多线程同时进入if (instance == null)时,会创建多个实例,违背单例原则。

2. 同步方法(线程安全,但性能差)

通过synchronized修饰getInstance()方法,强制同一时间只有一个线程执行实例创建逻辑:

复制代码
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    // 同步方法保证线程安全
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

优缺点

  • ✅ 线程安全,实现简单;
  • ❌ 每次调用getInstance()都需加锁,高并发场景下锁竞争导致性能损耗。
3. 双重检查锁(DCL,Double-Checked Locking)

结合 "两次判空 + volatile" 优化,既保证线程安全,又减少锁开销,是懒汉式的经典优化方案:

复制代码
public class LazySingleton {
    // 必须用volatile修饰:防止指令重排序导致的"半初始化实例"问题
    private static volatile LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        // 第一次判空:避免不必要的锁(大部分情况实例已存在)
        if (instance == null) {
            synchronized (LazySingleton.class) { // 类对象作为锁
                // 第二次判空:防止多个线程等待锁后重复创建
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

关键细节

  • volatile的作用:instance = new LazySingleton()会被拆分为 "分配内存→初始化实例→赋值引用" 三步,volatile禁止指令重排序,避免其他线程获取到 "未完全初始化的实例"(导致空指针)。
  • 双重判空:外层判空减少锁竞争,内层判空保证唯一实例。优缺点
  • ✅ 线程安全,高并发下性能优异;
  • ❌ 实现稍复杂,需注意volatile的使用(Java 5 + 才支持volatile禁止重排序)。
4. 静态内部类(最优实现)

利用 JVM 类加载机制实现懒加载 + 线程安全,是懒汉式的最优写法:

复制代码
public class LazySingleton {
    // 私有静态内部类:外部类加载时不会初始化,实现懒加载
    private static class SingletonHolder {
        // 静态常量:JVM保证类加载时的线程安全,仅初始化一次
        private static final LazySingleton INSTANCE = new LazySingleton();
    }

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        // 首次调用时,加载SingletonHolder并初始化INSTANCE
        return SingletonHolder.INSTANCE;
    }
}

原理

  • 外部类LazySingleton加载时,内部类SingletonHolder不会被加载;
  • 首次调用getInstance()时,SingletonHolder才会被加载,JVM 保证类加载的线程安全性,因此INSTANCE仅创建一次。优缺点
  • ✅ 线程安全、懒加载、实现简洁、无性能损耗;
  • ❌ 无法通过反射以外的方式传递初始化参数(如动态配置)。

三、懒汉式的应用场景

  • 实例占用资源大且使用频率低:如数据库连接池、Redis 缓存客户端(避免程序启动时就创建重量级资源);
  • 初始化依赖动态参数:如需要根据运行时配置初始化实例(饿汉式类加载时参数可能未就绪);
  • 高并发但实例创建后访问频繁:双重检查锁或静态内部类实现可平衡线程安全与性能。

四、懒汉式 vs 饿汉式

特性 懒汉式 饿汉式
实例化时机 首次调用getInstance() 类加载时
线程安全 需手动保证(如 DCL、静态内部类) 天然线程安全(JVM 类加载机制)
资源利用率 高(按需初始化) 低(可能提前初始化闲置资源)
实现复杂度 稍复杂(需处理线程安全) 简单

总结

懒汉式单例模式通过延迟初始化 优化资源利用,其中静态内部类 实现是最优选择(线程安全、简洁高效),而双重检查锁适合需要动态参数初始化的场景。实际开发中需根据资源占用、并发需求选择合适的实现方式,确保单例的唯一性和性能。

相关推荐
程序员游老板20 分钟前
基于SpringBoot3_vue3_MybatisPlus_Mysql_Maven的社区养老系统/养老院管理系统
java·spring boot·mysql·毕业设计·软件工程·信息与通信·毕设
福尔摩斯张39 分钟前
C++核心特性精讲:从C语言痛点出发,掌握现代C++编程精髓(超详细)
java·linux·c语言·数据结构·c++·驱动开发·算法
@淡 定1 小时前
Spring中@Autowired注解的实现原理
java·后端·spring
charlie1145141911 小时前
如何快速在 VS2026 上使用 C++ 模块 — 完整上手指南
开发语言·c++·笔记·学习·现代c++
时空无限1 小时前
Java Buildpack Reference
java·开发语言
爱笑的眼睛112 小时前
超越剪枝与量化:下一代AI模型压缩工具的技术演进与实践
java·人工智能·python·ai
阿里云云原生2 小时前
Android App 崩溃排查指南:阿里云 RUM 如何让你快速从告警到定位根因?
android·java
历程里程碑2 小时前
C++ 9 stack_queue:数据结构的核心奥秘
java·开发语言·数据结构·c++·windows·笔记·算法
醇氧2 小时前
【Windows】从守护到终结:解析一个 Java 服务的优雅停止脚本
java·开发语言·windows
努力发光的程序员2 小时前
互联网大厂Java求职面试实录
java·jvm·线程池·多线程·hashmap·juc·arraylist