闪电疯狂赛车
91.44M · 2026-03-23
单例模式是Java设计模式中最基础也最常用的创建型模式,核心目标是保证一个类在程序运行期间只有一个实例,并提供全局访问点。本文将详细拆解三种经典单例实现方式(饿汉式、懒汉式、双重校验锁),结合代码示例分析各自的优缺点与适用场景。
在实现单例前,需先明确三个核心约束:
new 关键字创建实例;所有单例实现都围绕这三个原则展开,区别仅在于实例创建时机和线程安全处理方式。
饿汉式有两种常见写法:静态变量版和静态代码块版,本质逻辑一致。
/**
* 饿汉式单例 - 静态变量版
* 类加载时直接创建实例,线程安全但可能浪费资源
*/
public class HungrySingleton {
// 1. 私有静态成员变量:类加载时初始化,全局唯一
private static final HungrySingleton INSTANCE = new HungrySingleton();
// 2. 私有化构造器:禁止外部new
private HungrySingleton() {
// 防止反射破坏单例(可选增强)
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
}
// 3. 公共静态方法:提供全局访问入口
public static HungrySingleton getInstance() {
return INSTANCE;
}
// 测试方法
public void sayHello() {
System.out.println("饿汉式单例:Hello Singleton!");
}
}
/**
* 饿汉式单例 - 静态代码块版
* 适合需要加载配置文件、初始化资源等复杂场景
*/
public class HungrySingletonWithStaticBlock {
// 1. 私有静态成员变量(先声明,后初始化)
private static final HungrySingletonWithStaticBlock INSTANCE;
// 2. 静态代码块:类加载时执行,完成实例初始化
static {
try {
// 模拟复杂初始化逻辑(如读取配置文件)
INSTANCE = new HungrySingletonWithStaticBlock();
} catch (Exception e) {
throw new RuntimeException("单例初始化失败", e);
}
}
// 3. 私有化构造器
private HungrySingletonWithStaticBlock() {
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
}
// 4. 公共静态方法
public static HungrySingletonWithStaticBlock getInstance() {
return INSTANCE;
}
public void sayHello() {
System.out.println("饿汉式单例(静态代码块):Hello Singleton!");
}
}
public class SingletonTest {
public static void main(String[] args) {
// 饿汉式测试
HungrySingleton instance1 = HungrySingleton.getInstance();
HungrySingleton instance2 = HungrySingleton.getInstance();
System.out.println(instance1 == instance2); // true(同一实例)
instance1.sayHello();
}
}
getInstance() 方法时才实例化,属于“懒加载”;/**
* 懒汉式单例 - 基础版(非线程安全)
* 仅适合单线程环境,多线程下会创建多个实例
*/
public class LazySingletonUnsafe {
// 1. 私有静态成员变量(不初始化)
private static LazySingletonUnsafe INSTANCE;
// 2. 私有化构造器
private LazySingletonUnsafe() {}
// 3. 公共静态方法:第一次调用时创建实例
public static LazySingletonUnsafe getInstance() {
if (INSTANCE == null) { // 判空:未创建则初始化
INSTANCE = new LazySingletonUnsafe();
}
return INSTANCE;
}
}
/**
* 懒汉式单例 - 加锁版(线程安全)
* 用synchronized保证线程安全,但每次调用都加锁,性能差
*/
public class LazySingletonSafe {
private static LazySingletonSafe INSTANCE;
private LazySingletonSafe() {}
// 方法上加synchronized,保证同一时间只有一个线程执行
public static synchronized LazySingletonSafe getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazySingletonSafe();
}
return INSTANCE;
}
public void sayHello() {
System.out.println("懒汉式单例(加锁版):Hello Singleton!");
}
}
if (INSTANCE == null) 会创建多个实例,破坏单例;synchronized 加在方法上,每次调用 getInstance() 都会加锁,即使实例已创建,仍会有锁竞争,性能损耗大。volatile 关键字,防止指令重排。/**
* 双重校验锁(DCL)单例 - 懒加载 + 线程安全 + 高性能
* 生产环境最常用的单例实现方式
*/
public class DclSingleton {
// 1. 私有静态成员变量:加volatile防止指令重排
private static volatile DclSingleton INSTANCE;
// 2. 私有化构造器
private DclSingleton() {
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
}
// 3. 公共静态方法:双重判空 + 局部锁
public static DclSingleton getInstance() {
// 第一次判空:实例已创建时,直接返回,无需加锁(提升性能)
if (INSTANCE == null) {
// 局部锁:仅实例未创建时加锁,减少锁竞争
synchronized (DclSingleton.class) {
// 第二次判空:防止多个线程等待锁后重复创建实例
if (INSTANCE == null) {
INSTANCE = new DclSingleton();
}
}
}
return INSTANCE;
}
public void sayHello() {
System.out.println("DCL单例:Hello Singleton!");
}
}
INSTANCE = new DclSingleton() 并非原子操作,JVM会拆分为3步:
若无 volatile,JVM可能发生指令重排(步骤2和3交换),导致线程A创建实例时,线程B看到 INSTANCE != null,但实例未完成初始化,进而获取到“半初始化”的实例,引发空指针异常。
volatile 关键字会禁止指令重排,保证实例创建的完整性。
public class SingletonTest {
public static void main(String[] args) {
// DCL单例测试(多线程环境)
for (int i = 0; i < 10; i++) {
new Thread(() -> {
DclSingleton instance = DclSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + instance);
}).start();
}
// 输出结果:所有线程获取的是同一个实例
}
}
| 实现方式 | 线程安全 | 懒加载 | 性能 | 适用场景 |
|---|---|---|---|---|
| 饿汉式 | 是 | 否 | 高 | 实例占用资源少、启动即使用 |
| 懒汉式(加锁) | 是 | 是 | 低 | 单线程环境、对性能要求低 |
| 双重校验锁(DCL) | 是 | 是 | 高 | 多线程环境、高性能要求(推荐) |
单例模式的核心是“唯一实例 + 全局访问”,三种经典实现各有侧重: