面试题答案
一键面试单例模式在多线程环境下的反模式示例
在多线程环境中,简单的单例模式实现可能出现问题。例如,如下代码:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在多线程情况下,当多个线程同时进入 if (instance == null)
判断时,可能会创建多个实例,这违背了单例模式的初衷,这就是一种反模式。
使用双重检查锁定机制重构
public class Singleton {
// volatile关键字确保可见性和禁止指令重排
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;
}
}
重构有效的原因
- 第一次检查:
if (instance == null)
用于避免不必要的同步开销。当实例已经被创建后,后续线程进入getInstance()
方法时,直接返回实例,无需进入同步块。 - 同步块:
synchronized (Singleton.class)
确保在同一时间只有一个线程能够进入此块,从而避免多个线程同时创建实例。 - 第二次检查:在同步块内再次检查
if (instance == null)
,是因为当第一个线程创建实例后,其他等待在同步块外的线程会再次获得进入同步块的机会。如果没有第二次检查,这些线程仍然会创建新的实例。 - volatile关键字:
volatile
修饰instance
变量,确保当一个线程修改instance
后,其他线程能立即看到修改后的结果,同时禁止指令重排。因为instance = new Singleton();
这行代码不是原子操作,可能会出现指令重排的情况,volatile
关键字能防止这种情况发生,保证单例模式在多线程环境下的正确性。