Java 基础-30-单例设计模式:懒汉式与饿汉式

在软件开发中,单例设计模式(Singleton Design Pattern)是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于管理共享资源(如数据库连接池、线程池等)或需要全局唯一实例的场景。

本文将详细介绍两种常见的单例实现方式:懒汉式饿汉式,并分析它们的优缺点及适用场景。


1. 单例模式的核心要素

要实现单例模式,需要满足以下三个条件:

  1. 私有化构造方法 :防止外部通过new关键字创建对象。
  2. 提供静态方法获取唯一实例:通过一个公共的静态方法返回唯一的实例。
  3. 保持单一实例:确保类中只有一个实例存在。

2. 饿汉式(Eager Initialization)

饿汉式是指在类加载时就立即创建实例。这种方式的特点是简单直接,但可能会造成资源浪费(如果实例从未被使用过)。

实现代码

复制代码
public class SingletonEager {
    // 1. 私有化构造方法
    private SingletonEager() {
        System.out.println("SingletonEager instance created");
    }

    // 2. 在类加载时创建唯一的实例
    private static final SingletonEager instance = new SingletonEager();

    // 3. 提供公共的静态方法获取实例
    public static SingletonEager getInstance() {
        return instance;
    }
}

特点

  • 优点
    • 简单易懂,实现方便。
    • 线程安全(因为实例在类加载时就已经创建,不存在多线程竞争问题)。
  • 缺点
    • 如果实例从未被使用过,会浪费内存资源。
    • 不适合需要延迟加载的场景。

3. 懒汉式(Lazy Initialization)

懒汉式 是指在第一次调用getInstance()方法时才创建实例。这种方式可以避免资源浪费,但需要注意线程安全问题。

实现代码(非线程安全版本)

复制代码
public class SingletonLazy {
    // 1. 私有化构造方法
    private SingletonLazy() {
        System.out.println("SingletonLazy instance created");
    }

    // 2. 定义静态变量,但不立即初始化
    private static SingletonLazy instance;

    // 3. 提供公共的静态方法获取实例
    public static SingletonLazy getInstance() {
        if (instance == null) { // 第一次检查
            instance = new SingletonLazy(); // 创建实例
        }
        return instance;
    }
}

特点

  • 优点
    • 延迟加载,节省资源。
  • 缺点
    • 存在线程安全问题(多线程环境下可能创建多个实例)。
线程安全改进版(双重检查锁定)

为了解决线程安全问题,可以使用双重检查锁定(Double-Checked Locking)机制:

复制代码
public class SingletonLazySafe {
    // 1. 私有化构造方法
    private SingletonLazySafe() {
        System.out.println("SingletonLazySafe instance created");
    }

    // 2. 使用volatile关键字保证可见性和禁止指令重排
    private static volatile SingletonLazySafe instance;

    // 3. 双重检查锁定
    public static SingletonLazySafe getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (SingletonLazySafe.class) {
                if (instance == null) { // 第二次检查
                    instance = new SingletonLazySafe();
                }
            }
        }
        return instance;
    }
}

特点

  • 优点
    • 延迟加载,节省资源。
    • 线程安全。
  • 缺点
    • 实现复杂度较高。

4. 对比:懒汉式 vs 饿汉式

特性 饿汉式 懒汉式
实例创建时机 类加载时 第一次调用getInstance()
资源占用 可能浪费资源 延迟加载,节省资源
线程安全性 天然线程安全 需额外处理(如双重检查锁定)
实现复杂度 简单 较复杂
适用场景 实例一定会被使用且对性能要求高 实例可能不会被使用或需延迟加载

5. 其他实现方式(扩展)

除了懒汉式和饿汉式,还有其他常见的单例实现方式,例如:

枚举单例

复制代码
public enum SingletonEnum {
    INSTANCE;

    public void doSomething() {
        System.out.println("Doing something...");
    }
}
  • 优点:天然线程安全,防止反射攻击,简洁优雅。
  • 缺点:功能有限,不适合需要继承的场景。

静态内部类

复制代码
public class SingletonInnerClass {
    private SingletonInnerClass() {}

    private static class SingletonHolder {
        private static final SingletonInnerClass INSTANCE = new SingletonInnerClass();
    }

    public static SingletonInnerClass getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
  • 优点:延迟加载,线程安全,性能较好。
  • 缺点:实现稍复杂。

6. 总结

  • 饿汉式适合于实例一定会被使用的场景,简单高效,但可能会浪费资源。
  • 懒汉式适合于实例可能不会被使用的场景,可以延迟加载,但需要注意线程安全问题。
  • 如果追求简洁和安全性,推荐使用枚举单例静态内部类实现。
相关推荐
无心水6 分钟前
【Java面试笔记:基础】8.对比Vector、ArrayList、LinkedList有何区别?
java·笔记·面试·vector·arraylist·linkedlist
农民也会写代码15 分钟前
dedecms织梦arclist标签noflag属性过滤多个参数
开发语言·数据库·sql·php·dedecms
创码小奇客25 分钟前
MongoDB 时间序列:解锁数据时光机的终极指南
java·mongodb·trae
黯_森25 分钟前
Java面向对象
java·后端
代码小侦探27 分钟前
Java中以Maven方式引入Oracle JDBC Driver依赖的详解
java·oracle·maven
不畏惧的少年28 分钟前
AQS的底层实现原理
java
内网渗透33 分钟前
Python 虚拟环境管理:venv 与 conda 的选择与配置
开发语言·python·conda·虚拟环境·venv
洛小豆1 小时前
饭票、图书馆、GC:这样理解 Java 引用,谁还不会?
java·后端·面试
SimonLiu0091 小时前
清理HiNas(海纳斯) Docker日志并限制日志大小
java·docker·容器
带刺的坐椅1 小时前
开发 MCP Proxy(代理)也可以用 Solon AI MCP 哟!
java·ai·llm·solon·mcp·mcp-server·mcp-client