MST

星途 面试题库

面试题:Java单例模式的线程安全与性能优化

在多线程环境下,常见的单例模式实现方式(饿汉式、懒汉式、双重检查锁、静态内部类、枚举)在性能和线程安全方面各有什么特点?如果要在高并发场景下使用单例模式,你会选择哪种实现方式并说明原因。
30.7万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

饿汉式

  • 性能:类加载时就初始化实例,可能会导致加载时间变长,若该单例对象占用资源多,会影响启动性能。但后续获取实例时速度快,因为无需判断是否初始化。
  • 线程安全:线程安全,因为在类加载阶段由 JVM 保证只创建一个实例。

懒汉式

  • 性能:延迟初始化,首次使用时才创建实例,在实例创建前性能较好。但每次获取实例都要进行同步检查,在多线程环境下频繁同步操作会带来性能开销。
  • 线程安全:如果不进行同步控制,是非线程安全的。若加上synchronized关键字修饰获取实例方法,可保证线程安全,但同步带来性能损耗。

双重检查锁

  • 性能:兼顾了懒加载和高性能。在第一次检查instance == null时,多数情况下不需要进入同步块,减少了同步开销,提升了性能。只有在第一次实例化时才会进入同步块,后续获取实例速度快。
  • 线程安全:在 JDK 1.5 之后,通过volatile关键字修饰实例变量,确保了指令重排序不会导致获取到未完全初始化的实例,保证了线程安全。

静态内部类

  • 性能:同样具有懒加载特性,在外部类加载时,静态内部类不会被加载,只有在调用获取实例方法时,静态内部类才会加载并初始化实例。由于类加载由 JVM 保证线程安全,且仅执行一次,后续获取实例性能好。
  • 线程安全:线程安全,由 JVM 类加载机制保证,静态内部类只会被加载一次,从而保证单例实例唯一。

枚举

  • 性能:在枚举类加载时就创建实例,与饿汉式类似,后续获取实例性能好。
  • 线程安全:线程安全,JVM 保证枚举实例的唯一性和线程安全性,同时还防止反序列化重新创建新的实例。

高并发场景选择及原因

在高并发场景下,推荐使用枚举或双重检查锁方式。

枚举:实现简单,由 JVM 保证线程安全和实例唯一性,并且天然防止反序列化创建新实例,性能上类加载后获取实例速度快,适用于大多数高并发场景。

双重检查锁:如果希望实现懒加载且对性能有较高要求,双重检查锁方式是不错的选择。它通过减少不必要的同步操作,在保证线程安全的同时,提升了获取实例的性能。不过代码实现相对复杂,需要注意volatile关键字的使用。