MST

星途 面试题库

面试题:Java并发编程中线程安全的单例模式实现

请使用Java实现一个线程安全的单例模式,并解释你所采用的方式为何能保证线程安全,比如双重检查锁定(DCL)、静态内部类等方式,同时说明它们各自的优缺点。
22.0万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. 饿汉式单例模式

public class HungrySingleton {
    private static final HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {}

    public static HungrySingleton getInstance() {
        return instance;
    }
}
  • 线程安全性:饿汉式在类加载时就创建实例,类加载过程由 JVM 保证线程安全,所以饿汉式天然线程安全。
  • 优点:实现简单,线程安全。
  • 缺点:如果实例化过程耗时较长,可能影响类加载速度,且不管是否使用该实例,都会在类加载时创建,浪费内存。

2. 懒汉式(线程不安全)

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
  • 线程安全性:这种方式在多线程环境下,多个线程可能同时进入 if (instance == null) 判断,导致创建多个实例,所以线程不安全。
  • 优点:延迟加载,只有在调用 getInstance 方法时才创建实例。
  • 缺点:线程不安全,不能在多线程环境下使用。

3. 懒汉式(线程安全 - 同步方法)

public class ThreadSafeLazySingleton {
    private static ThreadSafeLazySingleton instance;

    private ThreadSafeLazySingleton() {}

    public static synchronized ThreadSafeLazySingleton getInstance() {
        if (instance == null) {
            instance = new ThreadSafeLazySingleton();
        }
        return instance;
    }
}
  • 线程安全性:通过 synchronized 关键字修饰 getInstance 方法,保证同一时间只有一个线程能进入该方法,从而确保线程安全。
  • 优点:实现简单,保证线程安全且延迟加载。
  • 缺点:由于 synchronized 修饰整个方法,性能较低,每次调用 getInstance 方法都需要获取锁。

4. 双重检查锁定(DCL)

public class DCLSingleton {
    private volatile static DCLSingleton instance;

    private DCLSingleton() {}

    public static DCLSingleton getInstance() {
        if (instance == null) {
            synchronized (DCLSingleton.class) {
                if (instance == null) {
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}
  • 线程安全性volatile 关键字禁止指令重排,防止在实例化过程中,对象尚未完全初始化就被其他线程访问。第一次 if (instance == null) 检查是为了避免不必要的同步,第二次 if (instance == null) 检查是在获取锁后,确保只有一个线程创建实例,从而保证线程安全。
  • 优点:延迟加载,性能较高,只有第一次创建实例时需要同步。
  • 缺点:实现相对复杂,需要正确使用 volatile 关键字。

5. 静态内部类

public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {}

    private static class InnerClass {
        private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
    }

    public static StaticInnerClassSingleton getInstance() {
        return InnerClass.instance;
    }
}
  • 线程安全性:静态内部类只有在调用 getInstance 方法时才会加载,类加载过程由 JVM 保证线程安全,所以线程安全。
  • 优点:延迟加载,实现相对简单,性能较好。
  • 缺点:与饿汉式相比,理解起来稍复杂一些。