MST

星途 面试题库

面试题:Java单例模式在多线程环境下的优化

在高并发的多线程环境中,使用常规的懒汉式单例模式可能会出现问题。请详细说明如何优化懒汉式单例模式以适应高并发场景,同时分析优化后的方案对系统性能和资源的影响。
24.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

常规懒汉式单例模式问题

常规懒汉式单例模式在多线程高并发环境下,由于多个线程可能同时判断 instance == nulltrue,从而导致创建多个实例,破坏单例模式。

优化方案

  1. 同步方法: 在获取实例的方法上添加 synchronized 关键字。
public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

此方法虽然解决了线程安全问题,但每次调用 getInstance 方法都需要获取锁,在高并发环境下性能较低,因为锁的竞争会成为瓶颈。

  1. 双重检查锁定(DCL)
public class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

这里使用 volatile 关键字修饰 instance,保证了其可见性和禁止指令重排序。第一次 if (instance == null) 检查是为了避免不必要的锁竞争,只有在 instancenull 时才进入同步块。同步块内再次检查 instance == null,防止多个线程同时通过第一次检查进入同步块创建多个实例。相比同步方法,DCL 大大提高了性能,只有在第一次创建实例时会有锁竞争。

  1. 静态内部类
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 方法时,才会加载 SingletonHolder 类,从而创建单例实例。此方法既保证了线程安全,又避免了同步带来的性能开销,是一种比较优雅的解决方案。

对系统性能和资源的影响

  • 同步方法:性能较低,因为每次获取实例都需要竞争锁,导致线程等待,增加了线程上下文切换开销,在高并发场景下可能成为性能瓶颈。但实现简单,对资源消耗主要在锁竞争上。
  • 双重检查锁定(DCL):性能较好,只有首次创建实例时会有短暂的锁竞争,后续获取实例无需竞争锁。volatile 关键字虽有一定开销,但相比同步方法可忽略不计。对资源影响较小,主要开销在首次创建实例时的锁竞争。
  • 静态内部类:性能最优,利用类加载机制保证线程安全,无额外同步开销。对资源消耗极少,只在类加载时创建一次实例。