面试题答案
一键面试反模式描述
在多线程环境下,使用懒汉式单例模式且未进行适当同步控制时,可能出现反模式。例如以下代码:
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
当多个线程同时调用 getInstance()
方法,且 instance
为 null
时,可能会创建多个实例,破坏单例模式的唯一性。
避免方法
- 同步方法:在
getInstance()
方法上添加synchronized
关键字。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
这种方式虽然能保证线程安全,但每次调用 getInstance()
方法都要获取锁,性能较低。
- 双重检查锁定(DCL):结合
synchronized
和双重检查机制。
public class DoubleCheckedLockingSingleton {
private volatile static DoubleCheckedLockingSingleton instance;
private DoubleCheckedLockingSingleton() {}
public static DoubleCheckedLockingSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedLockingSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
这里使用 volatile
关键字保证 instance
的可见性,避免指令重排导致其他线程获取到未完全初始化的实例。第一次 if (instance == null)
检查避免不必要的同步,提高性能。
- 静态内部类:利用类加载机制实现线程安全的懒加载。
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {}
private static class SingletonHolder {
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
类加载时,SingletonHolder
不会立即加载,只有在调用 getInstance()
方法时才会加载并初始化 INSTANCE
,利用了类加载的线程安全性。
- 枚举单例:简单且线程安全。
public enum EnumSingleton {
INSTANCE;
}
枚举类型在Java中天然线程安全,并且在序列化和反序列化时也能保证单例。