背景
最近面试一家外企,面试官问我为什么静态内部类单例模式是线程安全的?
按我的理解,应该是类初始化的时候,维护了一个static boolean类型的变量,用来表示类有没有被初始化过,类初始化的时候会判断这个变量进而决定是否初始化。面试官也没说什么,但我总感觉少了点什么。于是查阅资料、记录一下。
代码样例
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {
}
public static StaticInnerClassSingleton getSingleton() {
return SingletonHolder.singleton;
}
private static class SingletonHolder {
private static final StaticInnerClassSingleton singleton = new StaticInnerClassSingleton();
}
}
参考回答
- 我们在静态内部类中使用饿汉式方式创建了一个静态常量/变量。
- JVM保证对于每一个类或接口C,都有一个唯一的
初始化锁LC
与之对应;保证一个类的类构造器在多线程环境中被正确的加锁、同步。- 如果多个线程同时去初始化一个类,那么只有一个线程能获取到初始化锁LC去执行这个类的类构造器。
- 其他的线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
- 并且每个线程至少获取一次初始化锁LC来确保这个类已经被初始化过了。
- 所以在同一个类加载器下,一个类只会被初始化一次。
- 而静态属性保证了全局唯一,静态变量初始化保证了线程安全。
注:
- 一个线程执行类初始化操作,其他线程虽然会被阻塞,但是执行类初始化的线程退出后,其他线程在被唤醒之后不会进入/执行<clinit>()方法。
- 如果一个类初始化特别慢,就可能造成其他线程阻塞很久,而这种阻塞往往在实际应用中是隐藏的。