本文目录导读:

我来给你详细讲解懒汉式(Lazy Initialization)单例模式,包括多种实现方式和完整代码案例。
什么是懒汉式单例?
懒汉式单例的特点是:实例在第一次被使用时才创建,而不是在类加载时就创建,这样可以延迟加载,节省资源。
基础懒汉式(线程不安全)
这是最简单的实现,但存在线程安全问题:
public class SingletonLazy {
// 私有静态实例,初始为null
private static SingletonLazy instance = null;
// 私有构造函数,防止外部实例化
private SingletonLazy() {
System.out.println("创建懒汉式单例");
}
// 公共的静态方法,用于获取实例
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
// 测试方法
public void showMessage() {
System.out.println("懒汉式单例工作正常!");
}
}
线程安全的懒汉式(方法同步)
通过synchronized关键字保证线程安全,但性能较低:
public class SingletonLazySync {
private static SingletonLazySync instance = null;
private SingletonLazySync() {
System.out.println("创建线程安全的懒汉式单例");
}
// 同步方法,保证线程安全
public static synchronized SingletonLazySync getInstance() {
if (instance == null) {
instance = new SingletonLazySync();
}
return instance;
}
public void showMessage() {
System.out.println("线程安全的懒汉式单例工作正常!");
}
}
双重检查锁(推荐)
最优化的线程安全实现,既保证线程安全,又保证性能:
public class SingletonDoubleChecked {
// volatile关键字保证内存可见性和禁止指令重排序
private static volatile SingletonDoubleChecked instance = null;
private SingletonDoubleChecked() {
// 防止通过反射创建实例
if (instance != null) {
throw new RuntimeException("不允许反射创建单例!");
}
System.out.println("创建双重检查锁的单例");
}
public static SingletonDoubleChecked getInstance() {
// 第一次检查:如果实例已存在,直接返回
if (instance == null) {
// 同步代码块,只锁住创建实例的部分
synchronized (SingletonDoubleChecked.class) {
// 第二次检查:防止多个线程同时创建实例
if (instance == null) {
instance = new SingletonDoubleChecked();
}
}
}
return instance;
}
public void showMessage() {
System.out.println("双重检查锁的单例工作正常!");
}
}
静态内部类方式(最推荐)
利用JVM的类加载机制,实现最简单的线程安全懒加载:
public class SingletonStaticInner {
// 私有构造函数
private SingletonStaticInner() {
System.out.println("创建静态内部类单例");
}
// 静态内部类,在第一次使用时加载
private static class SingletonHolder {
// final保证可见性
private static final SingletonStaticInner INSTANCE = new SingletonStaticInner();
}
// 获取实例的方法
public static SingletonStaticInner getInstance() {
return SingletonHolder.INSTANCE;
}
public void showMessage() {
System.out.println("静态内部类单例工作正常!");
}
}
完整测试案例
创建一个完整的测试程序,验证各种懒汉式实现:
public class LazySingletonTest {
// 测试1:验证单例的唯一性
public static void testSingleton() {
System.out.println("=== 测试1:验证单例唯一性 ===");
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println("s1 == s2? " + (s1 == s2)); // true
// 验证其他实现
SingletonDoubleChecked d1 = SingletonDoubleChecked.getInstance();
SingletonDoubleChecked d2 = SingletonDoubleChecked.getInstance();
System.out.println("双重检查锁: d1 == d2? " + (d1 == d2));
SingletonStaticInner i1 = SingletonStaticInner.getInstance();
SingletonStaticInner i2 = SingletonStaticInner.getInstance();
System.out.println("静态内部类: i1 == i2? " + (i1 == i2));
System.out.println();
}
// 测试2:验证懒加载特性(延迟加载)
public static void testLazyLoading() {
System.out.println("=== 测试2:验证懒加载特性 ===");
System.out.println("程序启动,但尚未调用getInstance()");
System.out.println("现在开始调用getInstance()...");
SingletonLazy instance = SingletonLazy.getInstance();
System.out.println("成功获取实例");
System.out.println();
}
// 测试3:多线程环境测试
public static void testMultiThread() {
System.out.println("=== 测试3:多线程环境测试 ===");
// 创建多个线程同时获取实例
int threadCount = 5;
Thread[] threads = new Thread[threadCount];
SingletonDoubleChecked[] instances = new SingletonDoubleChecked[threadCount];
for (int i = 0; i < threadCount; i++) {
final int index = i;
threads[i] = new Thread(() -> {
instances[index] = SingletonDoubleChecked.getInstance();
System.out.println("线程" + index + ": 获取实例成功,哈希值=" +
instances[index].hashCode());
});
}
// 启动所有线程
for (Thread t : threads) {
t.start();
}
// 等待所有线程结束
for (Thread t : threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 验证所有线程获取的是同一个实例
boolean isSame = true;
for (int i = 1; i < threadCount; i++) {
if (instances[i] != instances[0]) {
isSame = false;
break;
}
}
System.out.println("所有线程获取的实例是否相同? " + isSame);
System.out.println();
}
// 测试4:性能对比测试
public static void testPerformance() {
System.out.println("=== 测试4:性能对比测试 ===");
final int TEST_TIMES = 1000000;
// 测试双重检查锁
long startTime = System.currentTimeMillis();
for (int i = 0; i < TEST_TIMES; i++) {
SingletonDoubleChecked.getInstance();
}
long endTime = System.currentTimeMillis();
System.out.println("双重检查锁 " + TEST_TIMES + " 次获取耗时: "
+ (endTime - startTime) + "ms");
// 测试同步方法
startTime = System.currentTimeMillis();
for (int i = 0; i < TEST_TIMES; i++) {
SingletonLazySync.getInstance();
}
endTime = System.currentTimeMillis();
System.out.println("同步方法 " + TEST_TIMES + " 次获取耗时: "
+ (endTime - startTime) + "ms");
// 测试静态内部类
startTime = System.currentTimeMillis();
for (int i = 0; i < TEST_TIMES; i++) {
SingletonStaticInner.getInstance();
}
endTime = System.currentTimeMillis();
System.out.println("静态内部类 " + TEST_TIMES + " 次获取耗时: "
+ (endTime - startTime) + "ms");
System.out.println();
}
// 测试5:防御性测试(反射攻击防护)
public static void testReflectionAttack() {
System.out.println("=== 测试5:反射攻击防护测试 ===");
try {
// 尝试通过反射创建第二个实例
Class<?> clazz = Class.forName("SingletonDoubleChecked");
java.lang.reflect.Constructor<?> constructor =
clazz.getDeclaredConstructor();
constructor.setAccessible(true);
// 尝试创建新实例(应该失败)
SingletonDoubleChecked instance =
(SingletonDoubleChecked) constructor.newInstance();
System.out.println("警告!反射创建实例成功!");
} catch (Exception e) {
System.out.println("反射攻击被阻止:" + e.getMessage());
}
System.out.println();
}
// 主方法
public static void main(String[] args) {
System.out.println("========== 懒汉式单例模式测试 ==========\n");
testSingleton();
testLazyLoading();
testMultiThread();
testPerformance();
testReflectionAttack();
System.out.println("========== 测试完成 ==========");
}
}
饿汉式对比
为了更好理解,提供一个饿汉式对比:
public class SingletonEager {
// 类加载时就创建实例(饿汉式)
private static final SingletonEager INSTANCE = new SingletonEager();
private SingletonEager() {}
public static SingletonEager getInstance() {
return INSTANCE;
}
}
实现方式对比总结
| 实现方式 | 线程安全 | 懒加载 | 性能 | 推荐程度 |
|---|---|---|---|---|
| 基础懒汉式 | 高 | 不推荐 | ||
| 同步方法 | 低 | 不推荐 | ||
| 双重检查锁 | 高 | 推荐 | ||
| 静态内部类 | 高 | 最推荐 |
使用建议
- 开发环境:推荐使用静态内部类方式,代码最简洁,性能最好
- 有特殊需求:如果需要对实例初始化做特殊控制,使用双重检查锁方式
- 测试环境:可以使用基础的懒汉式,但不建议在生产环境使用
注意事项:
- 懒汉式适合创建开销大、可能需要延迟加载的场景
- 所有实现都要考虑线程安全问题
- 最好加上反射攻击的防护
- 如果有序列化需求,还需要添加
readResolve()方法
这就是懒汉式单例的完整实现和测试案例,希望能帮助你深入理解这个概念!