面试题答案
一键面试单例模式在Java多线程环境下可能出现的问题
- 多次实例化问题:在多线程环境下,如果多个线程同时检查到单例实例尚未创建,就可能会同时执行创建实例的代码,导致创建多个实例,破坏单例模式的唯一性。
优化策略及优缺点分析
- 饿汉式单例
- 实现方式:在类加载时就创建单例实例。
public class EagerSingleton { private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton() {} public static EagerSingleton getInstance() { return instance; } }
- 优点:实现简单,类加载时就创建实例,天然线程安全,不存在多线程同步问题。
- 缺点:如果单例实例占用资源较多,而应用程序可能一直不会使用该实例,会造成资源浪费。
- 懒汉式单例(双重检查锁)
- 实现方式:在第一次调用
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; } }
- 优点:延迟加载,只有在真正需要使用时才创建实例,提高了资源利用率。并且通过双重检查锁机制,在保证线程安全的同时,尽量减少了同步带来的性能开销。
- 缺点:实现相对复杂,需要注意
volatile
关键字的使用,以防止指令重排导致的问题。如果使用不当,可能仍然会出现线程安全问题。
- 实现方式:在第一次调用
- 静态内部类单例
- 实现方式:利用静态内部类的特性实现延迟加载和线程安全。
public class StaticInnerClassSingleton { private StaticInnerClassSingleton() {} private static class SingletonHolder { private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton(); } public static StaticInnerClassSingleton getInstance() { return SingletonHolder.INSTANCE; } }
- 优点:兼顾了懒汉式的延迟加载和饿汉式的线程安全,实现相对简洁。静态内部类只有在调用
getInstance
方法时才会被加载,保证了延迟加载;而类加载机制保证了实例创建的线程安全性。 - 缺点:在某些特定场景下(如需要在类加载时执行复杂初始化操作),可能不太适用。但对于大多数普通的单例场景,是一种非常优秀的实现方式。