MST

星途 面试题库

面试题:Java单例模式中懒加载与饿汉式加载在多线程环境下的表现差异

请阐述Java单例模式中懒加载和饿汉式加载在多线程环境下分别会出现什么问题,如何解决懒加载在多线程环境下的线程安全问题?
10.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

懒加载在多线程环境下的问题

在多线程环境中,懒加载(延迟加载)如果不进行特殊处理,会出现多个线程同时判断实例为 null,进而创建多个实例的情况,破坏单例模式的唯一性。

饿汉式加载在多线程环境下的问题

饿汉式加载在类加载时就创建实例,本身线程安全,不存在多线程并发创建实例的问题。但它可能会造成资源浪费,若单例对象一直未被使用,也会在类加载时创建。

解决懒加载在多线程环境下线程安全问题的方法

  1. 同步方法(Synchronized method): 在获取实例的方法上添加 synchronized 关键字,使得每次只有一个线程能进入该方法,从而保证线程安全。但这种方式性能较低,因为整个方法都被同步,每次调用都需要竞争锁。
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
  1. 双重检查锁定(Double - Checked Locking): 在同步方法的基础上,进行两次 null 检查。第一次检查是为了避免不必要的同步,第二次检查是在获取锁后,再次确认实例是否已被创建。这样既保证了线程安全,又提高了性能。
public class LazySingleton {
    private static volatile LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

这里使用 volatile 关键字是为了防止指令重排序,确保 instance = new LazySingleton() 这一操作完成后,其他线程能立刻看到最新的 instance 值。 3. 静态内部类(Static Inner Class): 利用类加载机制,在外部类被加载时,静态内部类不会被加载,只有在调用 getInstance 方法时,静态内部类才会被加载并创建实例。由于类加载机制本身是线程安全的,所以这种方式既实现了懒加载,又保证了线程安全。

public class LazySingleton {
    private LazySingleton() {}

    private static class SingletonHolder {
        private static final LazySingleton INSTANCE = new LazySingleton();
    }

    public static LazySingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}