懒汉式案例

wen java案例 3

本文目录导读:

懒汉式案例

  1. 什么是懒汉式单例?
  2. 基础懒汉式(线程不安全)
  3. 线程安全的懒汉式(方法同步)
  4. 双重检查锁(推荐)
  5. 静态内部类方式(最推荐)
  6. 完整测试案例
  7. 饿汉式对比
  8. 实现方式对比总结
  9. 使用建议

我来给你详细讲解懒汉式(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;
    }
}

实现方式对比总结

实现方式 线程安全 懒加载 性能 推荐程度
基础懒汉式 不推荐
同步方法 不推荐
双重检查锁 推荐
静态内部类 最推荐

使用建议

  1. 开发环境:推荐使用静态内部类方式,代码最简洁,性能最好
  2. 有特殊需求:如果需要对实例初始化做特殊控制,使用双重检查锁方式
  3. 测试环境:可以使用基础的懒汉式,但不建议在生产环境使用

注意事项:

  • 懒汉式适合创建开销大、可能需要延迟加载的场景
  • 所有实现都要考虑线程安全问题
  • 最好加上反射攻击的防护
  • 如果有序列化需求,还需要添加readResolve()方法

这就是懒汉式单例的完整实现和测试案例,希望能帮助你深入理解这个概念!

抱歉,评论功能暂时关闭!