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 类加载机制)
资源利用率 高(按需初始化) 低(可能提前初始化闲置资源)
实现复杂度 稍复杂(需处理线程安全) 简单

总结

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

相关推荐
hd51cc1 小时前
MFC多线程学习笔记一:用户界面线程和工作线程
笔记·学习·mfc
czhc11400756631 小时前
C# 1120抽象类 static
java·开发语言·c#
whltaoin1 小时前
【 Java微服务 】Spring Cloud Alibaba :Nacos 注册中心与配置中心全攻略(含服务发现、负载均衡与动态配置)
java·微服务·nacos·springcloud·注册中心·配置中心
你不是我我1 小时前
【Java 开发日记】有了解过 SpringBoot 的参数配置吗?
java·开发语言·spring boot
稚辉君.MCA_P8_Java1 小时前
Gemini永久会员 Java HotSpot 虚拟机(JVM)的优点
java·jvm·后端
ivanfor6661 小时前
多租户架构的三级权限体系:设计逻辑与精准控制实现
java·开发语言·数据库
讨厌下雨的天空2 小时前
Linux信号量
java·开发语言
先生沉默先2 小时前
Nginx 反向代理学习:单端口统一访问多服务
学习·nginx·arcgis
♡喜欢做梦2 小时前
MyBatis XML 配置文件:从配置规范到 CRUD 开发实践
xml·java·java-ee·mybatis