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

总结

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

相关推荐
宋情写几秒前
JavaAI05-Chain、MCP
java·人工智能
C++chaofan7 分钟前
Java 并发编程:synchronized 优化原理深度解析
java·开发语言·jvm·juc·synchronized·
better_liang14 分钟前
每日Java面试场景题知识点之-Docker容器化部署
java·docker·微服务·devops·容器化·企业级开发
悟空码字16 分钟前
SpringBoot整合Kafka,实现高可用消息队列集群
java·spring boot·后端
天天摸鱼的java工程师16 分钟前
从等电梯到写调度系统:一个Java程序员的脑洞实践
java·后端
sww_102622 分钟前
JVM基础学习
jvm·学习·测试工具
qq_124987075329 分钟前
基于springboot的仁和机构的体检预约系统的设计与实现(源码+论文+部署+安装)
java·spring boot·后端·mysql·spring·毕业设计·计算机毕业设计
开开心心_Every30 分钟前
免费进销存管理软件:云端本地双部署
java·游戏·微信·eclipse·pdf·excel·语音识别
虫小宝38 分钟前
优惠券app安全策略:基于OAuth2.0的第三方平台授权与数据保护
java
资生算法程序员_畅想家_剑魔42 分钟前
Java常见技术分享-29-Jackson JSON处理类详解
java·开发语言·json