面试题答案
一键面试运用Java单例模式实现关键资源单例化
- 饿汉式单例
public class SingletonResource { private static final SingletonResource INSTANCE = new SingletonResource(); private SingletonResource() {} public static SingletonResource getInstance() { return INSTANCE; } }
- 优点:类加载时就创建实例,线程安全。
- 缺点:如果实例创建开销大,会影响启动性能,且不管是否使用都会创建。
- 懒汉式单例(线程不安全)
public class SingletonResource { private static SingletonResource instance; private SingletonResource() {} public static SingletonResource getInstance() { if (instance == null) { instance = new SingletonResource(); } return instance; } }
- 优点:延迟加载,在需要时才创建实例。
- 缺点:线程不安全,在多线程环境下可能创建多个实例。
- 懒汉式单例(线程安全,同步方法)
public class SingletonResource { private static SingletonResource instance; private SingletonResource() {} public static synchronized SingletonResource getInstance() { if (instance == null) { instance = new SingletonResource(); } return instance; } }
- 优点:延迟加载且线程安全。
- 缺点:同步方法性能较低,每次调用
getInstance
方法都要获取锁。
- 双重检查锁(DCL)单例
public class SingletonResource { private static volatile SingletonResource instance; private SingletonResource() {} public static SingletonResource getInstance() { if (instance == null) { synchronized (SingletonResource.class) { if (instance == null) { instance = new SingletonResource(); } } } return instance; } }
- 优点:延迟加载,兼顾线程安全和性能。通过
volatile
关键字防止指令重排,双重检查锁减少不必要的同步开销。 - 缺点:实现复杂,早期JVM版本对
volatile
语义实现不完善可能有问题。
- 优点:延迟加载,兼顾线程安全和性能。通过
- 静态内部类单例
public class SingletonResource { private SingletonResource() {} private static class SingletonHolder { private static final SingletonResource INSTANCE = new SingletonResource(); } public static SingletonResource getInstance() { return SingletonHolder.INSTANCE; } }
- 优点:延迟加载,线程安全,由JVM保证。实现简洁,没有同步开销。
- 缺点:无明显缺点。
单例模式在复杂分布式微服务架构下的挑战及解决方案
- 挑战:分布式环境下的单例问题
- 在分布式系统中,每个微服务实例都有自己的JVM,传统单例模式只能保证在单个JVM内是单例,不同微服务实例会有多个单例实例。
- 解决方案:
- 使用分布式缓存:如Redis,将单例资源的状态存储在Redis中,各个微服务通过访问Redis获取和更新资源状态,保证一致性。
- 分布式锁:借助分布式锁服务(如Zookeeper、Redis实现的分布式锁),在创建单例资源时获取锁,只有获取到锁的微服务实例才能创建实例,其他实例等待,从而保证全局单例。
- 挑战:多线程并发访问
- 尽管单例模式在设计上考虑了线程安全,但在高并发场景下,同步机制可能成为性能瓶颈。
- 解决方案:
- 优化同步代码:如使用双重检查锁单例模式,减少不必要的同步块。
- 读写分离:对于读多写少的资源,可采用读写锁(
ReentrantReadWriteLock
),允许多个线程同时读,写操作时加排他锁。
- 挑战:资源释放和内存管理
- 单例实例在整个应用生命周期存在,如果资源占用大且长时间不释放,可能导致内存泄漏。
- 解决方案:
- 实现资源释放方法:在单例类中提供方法,在合适时机释放资源,如在应用关闭时调用。
- 弱引用或软引用:使用弱引用(
WeakReference
)或软引用(SoftReference
)来持有资源,当内存不足时,垃圾回收器可以回收资源。
- 挑战:与依赖注入框架集成
- 在微服务架构中常使用依赖注入框架(如Spring),单例模式的使用可能与框架的依赖管理机制冲突。
- 解决方案:
- 遵循框架规范:如在Spring中,使用
@Singleton
注解来标记单例Bean,框架会管理其生命周期和依赖注入。 - 自定义工厂类:结合依赖注入框架的扩展点,通过自定义工厂类创建单例实例,并集成到框架的依赖管理体系中。
- 遵循框架规范:如在Spring中,使用