面试题答案
一键面试懒加载在多线程环境下的问题
在多线程环境中,懒加载(延迟加载)如果不进行特殊处理,会出现多个线程同时判断实例为 null
,进而创建多个实例的情况,破坏单例模式的唯一性。
饿汉式加载在多线程环境下的问题
饿汉式加载在类加载时就创建实例,本身线程安全,不存在多线程并发创建实例的问题。但它可能会造成资源浪费,若单例对象一直未被使用,也会在类加载时创建。
解决懒加载在多线程环境下线程安全问题的方法
- 同步方法(Synchronized method):
在获取实例的方法上添加
synchronized
关键字,使得每次只有一个线程能进入该方法,从而保证线程安全。但这种方式性能较低,因为整个方法都被同步,每次调用都需要竞争锁。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
- 双重检查锁定(Double - Checked Locking):
在同步方法的基础上,进行两次
null
检查。第一次检查是为了避免不必要的同步,第二次检查是在获取锁后,再次确认实例是否已被创建。这样既保证了线程安全,又提高了性能。
public class LazySingleton {
private static volatile LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
这里使用 volatile
关键字是为了防止指令重排序,确保 instance = new LazySingleton()
这一操作完成后,其他线程能立刻看到最新的 instance
值。
3. 静态内部类(Static Inner Class):
利用类加载机制,在外部类被加载时,静态内部类不会被加载,只有在调用 getInstance
方法时,静态内部类才会被加载并创建实例。由于类加载机制本身是线程安全的,所以这种方式既实现了懒加载,又保证了线程安全。
public class LazySingleton {
private LazySingleton() {}
private static class SingletonHolder {
private static final LazySingleton INSTANCE = new LazySingleton();
}
public static LazySingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}