各种单例模式的实现方式

前言

之前在这篇 一文漫谈设计模式之创建型模式(一) 文章留了个坑位,现在来说完成下。

实现方式 线程安全 延迟加载 关键特点/原理 推荐指数
饿汉式 ✅ 安全 ❌ 不支持 类加载时即初始化实例。实现简单,但可能造成资源浪费。 ⭐⭐⭐
懒汉式(基础版) ❌ 不安全 ✅ 支持 首次调用时创建实例。实现简单,但多线程下不安全。
懒汉式(同步方法) ✅ 安全 ✅ 支持 getInstance方法上加synchronized锁。线程安全但效率低。 ⭐⭐
双重校验锁(DCL) ✅ 安全 ✅ 支持 两次检查实例是否为null,配合volatile关键字避免指令重排序。 ⭐⭐⭐⭐
静态内部类 ✅ 安全 ✅ 支持 利用类加载机制保证线程安全,由JVM在首次使用时加载内部类并初始化实例。 ⭐⭐⭐⭐⭐
枚举 ✅ 安全 ❌ 不支持(本质饿汉式) 写法简洁,由JVM保证实例唯一性,能防止反射和反序列化破坏单例。 ⭐⭐⭐⭐⭐

案例

饿汉式 (Eager Initialization)

java 复制代码
public class EagerSingleton {
    // 类加载时就初始化实例
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    // 私有构造器防止外部实例化
    private EagerSingleton() {
        System.out.println("EagerSingleton instance created!");
    }
    
    // 全局访问点
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
    
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

懒汉式 - 基础版 (非线程安全)

java 复制代码
public class LazySingleton {
    // 初始化为null,需要时才创建
    private static LazySingleton instance;
    
    private LazySingleton() {
        System.out.println("LazySingleton instance created!");
    }
    
    // 非线程安全的懒加载
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
    
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

懒汉式 - 同步方法版

java 复制代码
public class SynchronizedLazySingleton {
    private static SynchronizedLazySingleton instance;
    
    private SynchronizedLazySingleton() {
        System.out.println("SynchronizedLazySingleton instance created!");
    }
    
    // 在方法级别加synchronized保证线程安全
    public static synchronized SynchronizedLazySingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedLazySingleton();
        }
        return instance;
    }
    
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

双重校验锁 (DCL - Double Checked Locking)

java 复制代码
public class DCLSingleton {
    // volatile防止指令重排序
    private static volatile DCLSingleton instance;
    
    private DCLSingleton() {
        System.out.println("DCLSingleton instance created!");
    }
    
    public static DCLSingleton getInstance() {
        if (instance == null) {  // 第一次检查,避免不必要的同步
            synchronized (DCLSingleton.class) {  // 同步块
                if (instance == null) {  // 第二次检查
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
    
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

关键点解析

(1) volatile关键字的作用

禁止指令重排序:instance = new Singleton()在JVM中分为三步:1分配内存空间;2初始化对象;3将实例指向内存地址。

若未使用volatile,JVM可能按1->3->2的顺序执行(指令重排序)。此时若线程A执行到步骤3(未初始化),线程B在第一次检查时可能直接返回未初始化的实例,导致错误。

保证可见性:确保多线程环境下,一个线程对instance的修改能立即被其他线程看到。

(2) 双重检查的必要性:

第一次检查:避免每次调用getInstance都进入同步块,提升性能。

第二次检查:防止多个线程同时通过第一次检查后,重复创建实例。例如,线程A和B均通过第一次检查,A先获取锁并创建实例后,B再次获取锁时需第二次检查以避免重复创建。

静态内部类 (Static Inner Class / Holder)

java 复制代码
public class InnerClassSingleton {
    
    private InnerClassSingleton() {
        System.out.println("InnerClassSingleton instance created!");
    }
    
    // 静态内部类,只有在被引用时才会加载
    private static class SingletonHolder {
        private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }
    
    public static InnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;  // 这里才会触发内部类的加载
    }
    
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

枚举

java 复制代码
public enum EnumSingleton {
    INSTANCE;  // 枚举实例,由JVM保证唯一性
    
    // 可以添加任意方法
    public void doSomething() {
        System.out.println("EnumSingleton doing something...");
    }
    
    // 也可以有属性
    private String data;
    
    public String getData() {
        return data;
    }
    
    public void setData(String data) {
        this.data = data;
    }
}

// 使用方式:
// EnumSingleton singleton = EnumSingleton.INSTANCE;
// singleton.doSomething();

测试代码

java 复制代码
public class SingletonTest {
    public static void main(String[] args) {
        // 测试饿汉式
        EagerSingleton eager1 = EagerSingleton.getInstance();
        EagerSingleton eager2 = EagerSingleton.getInstance();
        System.out.println("EagerSingleton same instance? " + (eager1 == eager2));
        
        // 测试静态内部类
        InnerClassSingleton inner1 = InnerClassSingleton.getInstance();
        InnerClassSingleton inner2 = InnerClassSingleton.getInstance();
        System.out.println("InnerClassSingleton same instance? " + (inner1 == inner2));
        
        // 测试枚举
        EnumSingleton enum1 = EnumSingleton.INSTANCE;
        EnumSingleton enum2 = EnumSingleton.INSTANCE;
        System.out.println("EnumSingleton same instance? " + (enum1 == enum2));
        
        // 多线程测试
        testWithMultipleThreads();
    }
    
    private static void testWithMultipleThreads() {
        System.out.println("\nTesting with 10 threads:");
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazySingleton instance = LazySingleton.getInstance();
                System.out.println("Thread " + Thread.currentThread().getId() + 
                                 " got instance: " + instance.hashCode());
            }).start();
        }
    }
}

总结

关键要点说明:

饿汉式:最简单的实现,但可能在未使用时占用内存

基础懒汉式:在多线程环境下可能会创建多个实例

同步方法懒汉式:线程安全但性能差(每次调用都要同步)

双重校验锁:需要volatile关键字,适用于JDK 5+

静态内部类:优雅的延迟加载实现,推荐使用

枚举:最安全的实现,天然防止反射和序列化破坏

在实际开发中,如果不需要延迟加载,推荐使用枚举;如果需要延迟加载,推荐使用静态内部类。这两种方式都是《Effective Java》推荐的单例实现方式。

相关推荐
Chengbei118 小时前
红队专属Bing Dork自动化工具,敏感信息侦察效率拉满、自动生成可视化信息泄露审计报告
java·人工智能·安全·web安全·网络安全·自动化·系统安全
敖正炀8 小时前
集合-Set深入解析
java
下次再写9 小时前
Java互联网大厂面试技术问答实战:涵盖Java SE、Spring Boot、微服务及多场景应用
java·数据库·缓存·面试·springboot·microservices·技术问答
公众号-老炮说Java9 小时前
IDEA 2026.1 + Claude Code = 降维打击
java·ide·intellij-idea
千寻girling9 小时前
RabbitMQ 详细教程(38K字数)
java·后端·面试
止语Lab9 小时前
Go vs Java GC:同一场延迟战争的两条路
java·开发语言·golang
卷毛的技术笔记9 小时前
从“拆东墙补西墙”到“最终一致”:分布式事务在Spring Boot/Cloud中的破局之道
java·spring boot·分布式·后端·spring cloud·面试·rocketmq
ERBU DISH9 小时前
修改表字段属性,SQL总结
java·数据库·sql
云烟成雨TD10 小时前
Spring AI Alibaba 1.x 系列【26】Skills 生命周期深度解析
java·人工智能·spring
Pkmer10 小时前
古法编程: 深度解析Java调度器Timer
java·后端