Java中单例模式的几种实现方式总结

date
Aug 20, 2025
AI 摘要
slug
java-singleton-pattern-implementations
status
Published
tags
android
summary
单例模式(Singleton Pattern)是Java中最基本和最常用的设计模式之一。该模式确保一个类只有一个实例,并提供一个全局访问点来访问该实例。在需要频繁创建和销G毁对象或者需要一个全局唯一的对象(例如,线程池、缓存、日志对象等)的场景下,单例模式尤为重要。
type
Post
单例模式(Singleton Pattern)是Java中最基本和最常用的设计模式之一。该模式确保一个类只有一个实例,并提供一个全局访问点来访问该实例。在需要频繁创建和销G毁对象或者需要一个全局唯一的对象(例如,线程池、缓存、日志对象等)的场景下,单例模式尤为重要。
以下总结了Java中几种常见的单例模式实现方式,并分析了各自的优缺点。

1. 饿汉式(Eager Initialization)

饿汉式在类加载时就立即创建实例,因此是线程安全的。
 
优点:
  • 实现简单,代码可读性高。
  • 在类加载时就完成了实例化,避免了多线程同步问题,是线程安全的。
缺点:
  • 在类加载时就创建实例,即使后续从未使用过该实例,也会占用内存空间,可能造成资源浪费。

2. 懒汉式(Lazy Initialization)

懒汉式在第一次调用getInstance()方法时才创建实例。

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

优点:
  • 实现了懒加载,只有在需要时才创建实例,节约了资源。
缺点:
  • 在多线程环境下是非线程安全的。如果多个线程同时进入if (instance == null)判断,并且此时instance都为null,那么将会有多个线程创建出多个实例,违背了单例模式的初衷。

2.2 线程安全的懒汉式(同步方法)

通过在getInstance()方法上添加synchronized关键字,可以保证线程安全。
优点:
  • 解决了多线程环境下的线程安全问题。
缺点:
  • 每次调用getInstance()方法时都需要进行同步,即使实例已经被创建。这会导致不必要的性能开销,尤其是在高并发场景下。

3. 双重检查锁定(Double-Checked Locking)

双重检查锁定是对线程安全的懒汉式的一种优化,旨在减少不必要的同步开销。
关键点:
  • 使用volatile关键字修饰instance变量,确保其可见性和禁止指令重排。
  • 进行两次if (instance == null)检查,第一次检查是为了避免在实例已存在的情况下进入同步代码块,从而提高性能。第二次检查是为了在同步代码块内部再次确认实例是否被创建,防止多个线程同时通过第一次检查后重复创建实例。
优点:
  • 兼顾了线程安全和懒加载的性能。只有在第一次创建实例时才会进入同步代码块,后续的调用将直接返回已创建的实例,性能较高。
缺点:
  • 实现相对复杂,容易出错。volatile关键字是必不可少的,否则可能由于指令重排导致获取到未完全初始化的对象。

4. 【推荐】静态内部类(Static Inner Class)

这是目前被广泛推荐的一种实现方式,它利用了JVM类加载机制来保证线程安全和懒加载。
原理:
  • StaticInnerClassSingleton类被加载时,其静态内部类SingletonHolder并不会被加载。
  • 只有当第一次调用getInstance()方法时,JVM才会加载SingletonHolder类,并初始化INSTANCE
  • JVM在加载类时是线程安全的,因此保证了只有一个线程可以初始化INSTANCE
优点:
  • 实现了懒加载。
  • 由JVM保证线程安全,无需任何同步代码,性能高。
  • 实现简单,代码清晰。

5. 枚举(Enum)

这是《Effective Java》作者Joshua Bloch推荐的最佳实现方式。
优点:
  • 实现最简单。
  • 天然线程安全。
  • 能有效防止通过反射和反序列化创建新的对象,从而破坏单例。
缺点:
  • 不具备懒加载的特性。

总结

实现方式
线程安全
懒加载
推荐指数
备注
饿汉式
⭐⭐⭐
实现简单,但可能造成资源浪费。
懒汉式(非线程安全)
仅限单线程环境使用。
懒汉式(同步方法)
⭐⭐
性能开销较大,不推荐。
双重检查锁定
⭐⭐⭐⭐
实现较复杂,需正确使用volatile
静态内部类
⭐⭐⭐⭐⭐
兼具懒加载和线程安全,实现简单,推荐使用。
枚举
⭐⭐⭐⭐⭐
最简洁、最安全的实现方式,能防止反射和反序列化攻击。
在实际开发中,静态内部类枚举是两种最被推崇的单例模式实现方式。如果需要懒加载,静态内部类是绝佳的选择;如果不需要懒加载,或者对防止反射和反序列化有强烈的需求,枚举则是最简单且最安全的方式。

© CodeXun 2025