经典的单例模式保证只创建一个对象实例。在Java中有几种实现方式,但考虑到多线程和序列化,需要注意以下细节:
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; } }
enum EnumSingleton { INSTANCE; // 方法 }
private Object readResolve() { return getInstance(); }
仅使用 synchronized 方法 getInstance() 是否足以实现线程安全的单例?
答案: 是的,但这种方法会导致性能下降,因为每次调用 getInstance 时都会调用 synchronized。使用双检锁 + volatile 来处理 instance,或者使用枚举来实现单例会更有效。
低效代码示例:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
历史
在金融系统中实现单例时,忘记在双检锁中为 instance 引用添加
volatile。结果在高负载下随机出现创建两个类实例的情况,导致报告不一致。
历史
在日志库中通过私有静态对象实现了单例,但在序列化和后续反序列化(例如,在集群环境中)时出现了多个实例。通过添加 readResolve() 解决了问题。
历史
在分析系统中,开发人员通过 synchronized 方法 getInstance() 实现了线程安全的单例。在高负载系统的高峰时,性能突然下降,发现:由于不必要的同步,getInstance() 的调用(每秒数千次)互相阻塞,尽管初始化只需进行一次。