Java单例模式七种写法:从基础到线程安全最佳实践

星野
星野
发布时间:2026-10-16| 5人浏览过
原创

单例模式确保一个类在进程中仅有一个实例,提供全局访问点。本文深入剖析Java中单例模式的七种写法,包括饿汉式、懒汉式、双重校验锁等,重点讲解如何通过`synchronized`和`volatile`解决多线程下的线程安全问题,并对比各方案的优缺点与适用场景。

单例模式核心概念与利弊

单例模式(Singleton Pattern)的核心目标是确保当前进程中某个类只有一个实例对象。正如成语“天无二日”所喻,这种设计在全局范围内严格控制对象的创建。

应用场景

  • 需要生成唯一序列号的环境
  • 项目内需要共享访问点或数据(如配置管理器)
  • 对象创建消耗资源极大(如数据库连接池)
  • 工具类或静态常量定义

优势与局限

优势:内存中仅存一个实例,减少内存开销;避免对资源的多重占用;降低系统性能损耗。

局限:缺乏接口导致扩展困难;难以进行单元测试;可能与单一职责原则产生冲突。

1. 饿汉式(Hungry Singleton)

这是最简单的实现方式。类加载时便完成实例化,不存在延迟加载。

public class Singleton_1 {
    // 类加载即初始化,线程安全
    private static final Singleton_1 instance = new Singleton_1();

    private Singleton_1() {}

    public static Singleton_1 getInstance() {
        return instance;
    }
}

特点:

  • 线程安全:由于依赖JVM类加载机制保证初始化,天然线程安全。
  • 缺点:无论是否使用,启动时就会占用内存,属于“非懒加载”。

2. 懒汉式(Lazy Singleton - 线程不安全)

仅在调用getInstance方法时才创建实例,实现了延迟加载。

public class Singleton_2 {
    private static Singleton_2 instance;

    private Singleton_2() {}

    public static Singleton_2 getInstance() {
        if (instance == null) {
            instance = new Singleton_2();
        }
        return instance;
    }
}

风险:此写法在多线程环境下是不安全的。

当两个线程同时进入if (instance == null)判断时,可能分别创建一个实例,导致单例失效。

3. 懒汉式(加锁版)

为了解决线程安全问题,直接在方法上添加synchronized关键字。

public synchronized static Singleton_3 getInstance() {
    if (instance == null) {
        instance = new Singleton_3();
    }
    return instance;
}

评价:虽然解决了线程安全,但每次调用getInstance都需要获取锁,导致严重的性能浪费。这并非最优解。

4. 双重校验锁(DCL)

结合延迟加载与高性能需求,推荐采用双重校验锁(Double-Checked Locking)。

public class Singleton_4 {
    // volatile防止指令重排
    private static volatile Singleton_4 instance;

    private Singleton_4() {}

    public static Singleton_4 getInstance() {
        // 第一重校验:非空检查,避免不必要的同步
        if (instance == null) {
            // 同步块
            synchronized (Singleton_4.class) {
                // 第二重校验:确保只有一个线程能创建实例
                if (instance == null) {
                    instance = new Singleton_4();
                }
            }
        }
        return instance;
    }
}

关键原理分析

为什么需要双重校验?

若没有第二次if (instance == null)判断,在高并发下,第一个线程获取锁并实例化后释放锁,第二个线程进入同步块时不再检查,会再次创建新实例,破坏单例。

为什么必须使用volatile?

语句instance = new Singleton()并非原子操作,它在JVM层面分为三步:

  1. 分配内存空间
  2. 初始化对象
  3. 将引用指向内存地址

在没有volatile的情况下,编译器可能发生指令重排序,执行顺序变为:分配内存 → 引用指向内存 → 初始化对象。

假设线程A执行完第一步和第三步(未完成初始化),此时线程B进入。第一次校验发现instance != null,直接返回instance给线程B。线程B使用的将是一个初始化未完成的对象,导致程序崩溃。

加入volatile可禁止指令重排序,并保证内存可见性,确保只有完全初始化的对象才能被其他线程读取。

5. 静态内部类(Static Inner Class)

利用JVM类加载机制实现线程安全与懒加载的结合,是业界推荐的优雅写法。

public class Singleton_5 {
    private Singleton_5() {}

    private static class InnerSingleton {
        private static final Singleton_5 instance = new Singleton_5();
    }

    public static Singleton_5 getInstance() {
        return InnerSingleton.instance;
    }
}

原理:

  • 外部类Singleton_5加载时,不会初始化内部类。
  • 只有在调用getInstance时,才会触发InnerSingleton的加载。
  • JVM保证类加载过程的线程安全性,因此无需加锁即可实现线程安全的懒加载。

6. CAS原子操作(Atomic Reference)

基于乐观锁思想的变体实现,不使用传统的重量级锁。

import java.util.concurrent.atomic.AtomicReference;

public class Singleton_6 {
    private static final AtomicReference<Singleton_6> INSTANCE = new AtomicReference<>();

    private Singleton_6() {}

    public static Singleton_6 getInstance() {
        while (true) {
            Singleton_6 instance = INSTANCE.get();
            if (null == instance) {
                INSTANCE.compareAndSet(null, new Singleton_6());
            }
            return INSTANCE.get();
        }
    }
}

评价:这是一种较为罕见的写法。相比悲观锁(synchronized),它更轻量。但如果竞争激烈,忙等(busy-wait)会消耗大量CPU资源。

7. 枚举单例(Enum Singleton)

《Effective Java》作者Joshua Bloch极力推崇的实现方式。

public enum Singleton_7 {
    INSTANCE;

    public void anyMethod(){
        System.out.println("do anything");
    }
}

调用方式:Singleton_7.INSTANCE.anyMethod();

优势:不仅天然支持序列化,而且彻底杜绝了通过反射或反序列化破坏单例的可能,是解决一切单例问题的终极方案,尽管代码最为简练。

总结与最佳实践建议

在实际开发中:

  • 无延迟加载需求:直接使用饿汉式,简单且高效。
  • 有延迟加载需求:首选静态内部类法,兼顾懒加载与线程安全;或采用DCL双重校验锁(需注意volatile)。若想追求极致的防破解能力,推荐使用枚举。

面试考察重点通常在于理解DCL为何要加volatile以及指令重排的具体危害。以上就是Java单例模式七种写法的详细内容,更多关于Java设计模式实战的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。