MST

星途 面试题库

面试题:Java多线程环境下的单例模式与线程安全

在Java多线程编程中,实现一个线程安全的单例模式有多种方式。请详细描述饿汉式、懒汉式(双重检查锁定)单例模式,并分析它们在处理线程安全问题上的优缺点。同时,说明在Java 5.0及之后的版本中,使用枚举实现单例模式为何能保证线程安全。
33.3万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

饿汉式单例模式

  1. 实现方式:在类加载时就创建单例实例,由于类加载机制本身是线程安全的,所以这种方式天然线程安全。
public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    private EagerSingleton() {}
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}
  1. 优点:实现简单,不存在线程安全问题,在类加载时就完成实例化,避免了多线程并发访问时的同步问题。
  2. 缺点:如果单例对象创建开销较大,而应用程序可能一直不会使用该实例,就会造成资源浪费。因为在类加载时就创建了实例,不管是否需要使用。

懒汉式(双重检查锁定)单例模式

  1. 实现方式:在第一次调用 getInstance 方法时才创建实例,并且通过双重检查锁定机制确保多线程环境下只有一个实例被创建。
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;
    }
}
  1. 优点:延迟加载,只有在真正需要使用时才创建实例,节省资源。通过双重检查锁定,提高了并发性能,减少了不必要的同步开销。
  2. 缺点:实现相对复杂,需要注意 volatile 关键字的使用。如果不使用 volatile,可能会因为指令重排序导致在未完全初始化时就被其他线程访问到。

Java 5.0及之后版本中枚举实现单例模式保证线程安全的原因

  1. 实现方式
public enum EnumSingleton {
    INSTANCE;
    // 可以添加其他方法和属性
    public void doSomething() {
        System.out.println("Doing something...");
    }
}
  1. 保证线程安全的原因
    • 类加载机制:枚举类型在Java中是特殊的类,其加载机制保证了线程安全。在类加载过程中,JVM会保证枚举实例的唯一性,并且只会实例化一次。
    • 防止反射和反序列化破坏单例:枚举类型默认不支持反射创建新实例,并且在反序列化时,JVM会直接返回已有的枚举实例,而不会创建新的实例,从而保证了单例的唯一性和线程安全。