首先要关注并发编程中的三大特性
- 线程切换带来的原子性问题
- 指令重排序优化 (填充流水线, 尽最大可能发挥指令级并行的特性), 以及内存写回延迟 导致的顺序性问题.
- 多核多cache导致的可见性问题
针对上面三种问题来分析双检锁的实现方式
public class Singleton {
private static volatile Singleton instance;
private static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
首先, 原子性问题的实现是由synchronized关键字来保证, synchronized 保证对象初始化操作是线程间互斥的, 不会在一个线程new Singleton的同时, 另一个线程也进入这个过程中, 从而产生多个单例
可见性问题也是由 synchronized 来保证(volatile来保证也有道理…), synchronized 实现了两条语义
1. 线程加锁时, 将工作内存置无效, 共享变量都从工作内存读取
2. 线程解锁时, 将共享变量刷新到主内存
有序性 问题由 volatile 保证, 禁止指令重排序, 将instance的赋值操作固定到init
操作之后