实现线程安全的单例模式
字数 725 2025-11-06 12:41:20

实现线程安全的单例模式

题目描述
单例模式是一种常见的设计模式,确保一个类只有一个实例,并提供一个全局访问点。在多线程环境下,需要保证单例的线程安全性,避免创建多个实例。本题要求实现一个线程安全的单例模式,并解释其原理。

关键知识点

  • 懒汉式(Lazy Initialization)与饿汉式(Eager Initialization)
  • 双重检查锁定(Double-Checked Locking)
  • 静态内部类(Static Inner Class)
  • volatile关键字的作用

解题步骤

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

    • 延迟创建实例,但多线程下可能重复创建。
    public class Singleton {
        private static Singleton instance;
        private Singleton() {}  // 私有构造函数
        public static Singleton getInstance() {
            if (instance == null) {  // 线程A和B可能同时进入此条件
                instance = new Singleton();
            }
            return instance;
        }
    }
    
    • 问题:多个线程同时通过instance == null检查时,会创建多个实例。
  2. 懒汉式+同步方法(线程安全但效率低)

    • 通过synchronized保证线程安全,但每次调用都加锁,性能差。
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
    
  3. 双重检查锁定(DCL)

    • 在加锁前后各检查一次实例是否存在,避免不必要的锁竞争。
    public class Singleton {
        private static volatile Singleton instance;  // 必须使用volatile
        private Singleton() {}
        public static Singleton getInstance() {
            if (instance == null) {              // 第一次检查
                synchronized (Singleton.class) { // 加锁
                    if (instance == null) {      // 第二次检查
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    
    • volatile的作用:禁止指令重排序,防止其他线程获取到未初始化完成的对象(详见JVM的“指令重排序”问题)。
  4. 静态内部类(推荐方案)

    • 利用JVM类加载机制保证线程安全,且实现懒加载。
    public class Singleton {
        private Singleton() {}
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
        public static Singleton getInstance() {
            return SingletonHolder.INSTANCE;  // 首次调用时加载内部类
        }
    }
    
    • 原理:JVM在加载类时是线程安全的,静态内部类只在getInstance()首次调用时被加载,且静态变量初始化只执行一次。
  5. 枚举单例(最简洁的线程安全方案)

    • 枚举实例由JVM保证唯一性,且能防止反射攻击。
    public enum Singleton {
        INSTANCE;
        public void doSomething() { ... }
    }
    

总结

  • 懒汉式DCL需注意volatile的使用,避免指令重排序。
  • 静态内部类方案无需显式同步,代码简洁且高效。
  • 枚举单例是《Effective Java》推荐的方式,但某些场景下不够灵活(如需继承其他类)。
实现线程安全的单例模式 题目描述 单例模式是一种常见的设计模式,确保一个类只有一个实例,并提供一个全局访问点。在多线程环境下,需要保证单例的线程安全性,避免创建多个实例。本题要求实现一个线程安全的单例模式,并解释其原理。 关键知识点 懒汉式(Lazy Initialization)与饿汉式(Eager Initialization) 双重检查锁定(Double-Checked Locking) 静态内部类(Static Inner Class) volatile关键字的作用 解题步骤 基础懒汉式(非线程安全) 延迟创建实例,但多线程下可能重复创建。 问题 :多个线程同时通过 instance == null 检查时,会创建多个实例。 懒汉式+同步方法(线程安全但效率低) 通过 synchronized 保证线程安全,但每次调用都加锁,性能差。 双重检查锁定(DCL) 在加锁前后各检查一次实例是否存在,避免不必要的锁竞争。 volatile的作用 :禁止指令重排序,防止其他线程获取到未初始化完成的对象(详见JVM的“指令重排序”问题)。 静态内部类(推荐方案) 利用JVM类加载机制保证线程安全,且实现懒加载。 原理 :JVM在加载类时是线程安全的,静态内部类只在 getInstance() 首次调用时被加载,且静态变量初始化只执行一次。 枚举单例(最简洁的线程安全方案) 枚举实例由JVM保证唯一性,且能防止反射攻击。 总结 懒汉式DCL需注意 volatile 的使用,避免指令重排序。 静态内部类方案无需显式同步,代码简洁且高效。 枚举单例是《Effective Java》推荐的方式,但某些场景下不够灵活(如需继承其他类)。