Java:实现单例模式(线程安全的)算法(附带源码)
一、项目背景详细介绍
在 Java 应用中,单例模式(Singleton)是一种常见设计模式,用于确保全局只有一个实例并提供全局访问点。典型应用场景包括日志组件(LogManager)、配置管理(ConfigManager)、线程池(ThreadPoolManager)等。
随着多线程并发需求的增加,传统的单例实现容易出现“多线程下创建多实例”的缺陷,进而导致应用状态不可控、资源浪费甚至数据竞态。因此,设计一种既简洁又能在高并发场景下保证线程安全、且不会带来性能瓶颈的单例模式实现,就显得尤为重要。
二、项目需求详细介绍
-
功能需求
-
全局只允许创建一次实例;
-
在多线程高并发环境下永不产生第二个实例;
-
延迟加载(Lazy Initialization)或饿汉加载(Eager Initialization)两种模式任选其一;
-
-
性能需求
-
获取实例时的开销要小;
-
不使用过度同步,避免锁竞争成为性能瓶颈;
-
-
设计需求
-
提供至少三种线程安全的实现方式:
-
双重检查锁(Double‑Checked Locking) +
volatile; -
静态内部类(Initialization‑On‑Demand Holder Idiom);
-
枚举(Enum Singleton);
-
-
代码集中在一个文件中,不同实现用注释区分;
-
每种方式均要有详细注释说明其原理和优缺点;
-
三、相关技术详细介绍
-
Java 内存模型(JMM)与
volatile-
volatile保证可见性和有序性,防止指令重排序导致的“半初始化”问题;
-
-
双重检查锁
-
通过两次判 null 加锁检查,减少同步范围;
-
-
静态内部类
-
利用类加载的“初始化锁”天然线程安全且延迟加载;
-
-
枚举单例
-
Java 枚举由 JVM 保证只加载一次,自动防反序列化、反射攻击;
-
四、实现思路详细介绍
-
双重检查锁
-
私有构造、私有静态
volatile实例; -
getInstance()先判 null,不为 null 直接返回;否则同步,内部再判 null 并创建;
-
-
静态内部类
-
外部类私有构造;
-
内部静态类持有静态实例;
-
getInstance()直接返回内部类的实例;
-
-
枚举
-
定义单元素枚举
INSTANCE; -
JVM 保证线程安全与反射/序列化安全;
-
五、完整实现代码
package com.example.singleton;
/**
* 多种线程安全单例模式实现示例
*/
public class SingletonDemo {
// --------------------------------------------
// 1. 双重检查锁(Double‑Checked Locking)+ volatile
// --------------------------------------------
private static volatile SingletonDemo instanceDCL;
private SingletonDemo() {
// 防止反射
if (instanceDCL != null) {
throw new RuntimeException("禁止反射调用");
}
}
public static SingletonDemo getInstanceDCL() {
if (instanceDCL == null) {
synchronized (SingletonDemo.class) {
if (instanceDCL == null) {
instanceDCL = new SingletonDemo();
}
}
}
return instanceDCL;
}
// --------------------------------------------
// 2. 静态内部类(Initialization‑On‑Demand Holder)
// --------------------------------------------
private static class Holder {
private static final SingletonDemo INSTANCE = new SingletonDemo();
}
public static SingletonDemo getInstanceHolder() {
return Holder.INSTANCE;
}
// --------------------------------------------
// 3. 枚举单例(最简洁、安全)
// --------------------------------------------
public enum SingletonEnum {
INSTANCE;
private final SingletonDemo instance;
SingletonEnum() {
instance = new SingletonDemo();
}
public SingletonDemo getInstance() {
return instance;
}
}
}
六、代码详细解读
-
双重检查锁
-
第一重:避免每次都进入
synchronized; -
第二重:保证只有第一次创建时加锁;
-
volatile:防止指令重排序,避免读到未初始化对象;
-
-
静态内部类
-
利用 JVM 类加载机制,只有在第一次调用
getInstanceHolder()时才初始化Holder,线程安全且延迟加载;
-
-
枚举单例
-
enum是 JVM 级别保证的单例,不会被反射和序列化破坏;
-
七、项目详细总结
本文展示了三种常用且线程安全的 Java 单例模式实现:双重检查锁、静态内部类与枚举。在多线程场景中,推荐使用静态内部类或枚举方式,因为它们更简洁、性能更优、易于防御复杂攻击。双重检查锁虽然也可行,但须仔细使用 volatile 并注意反射攻击防护。
八、项目常见问题及解答
-
Q:为什么需要
volatile?
A:防止指令重排序导致其他线程读到尚未初始化完毕的instanceDCL。 -
Q:双重检查锁是否有性能问题?
A:现代 JVM 优化后同步开销很低,但比静态内部类略高。 -
Q:反射如何破坏单例?
A:反射可调用私有构造器创建第二个实例,需在构造器中判断并抛异常。 -
Q:序列化如何破坏单例?
A:枚举单例自动防止,其他方式需实现readResolve()。 -
Q:哪种方式最推荐?
A:枚举方式最安全,其次静态内部类;双重检查锁次之。
九、扩展方向与性能优化
-
添加
readResolve():防止反序列化破坏; -
防止反射:在私有构造器中加入
if (instance != null) throw…; -
测试覆盖:用多线程并发测试工具(如 JMH)验证性能与线程安全;
-
复杂场景:带参数的单例工厂、可延迟销毁的缓存单例等扩展;
-
库化发布:封装通用单例工具类,支持多种初始化策略。
更多推荐

所有评论(0)