知识卡片

DCL必须配volatile

普通读书笔记卡 · 1536

内容

双重检查锁(DCL)单例的关键不是”检查两次”的语法习惯,而是 instance = new Singleton() 可被拆成三步:分配内存、初始化对象、赋值给 instance。无 volatile 时可能被重排为”分配→赋值→初始化”:线程 A 赋值后未初始化就被切走,线程 B 见非空直接返回未初始化对象。加 volatile 后 [[volatile传递信号]] 的屏障会阻止重排。

参考来源

- 位置:《Java高并发核心编程.卷2,多线程、锁、JMM、JUC、高并发设计模式》第8章《高并发设计模式》8.1.4节《使用双重检查锁+volatile》(源文件:_epub-src/OEBPS/Text/chapter261.xhtml) - 结论依据:原文把`instance = new Singleton()`拆解为“分配一块内存M”“在内存M上初始化Singleton对象”“M的地址赋值给instance变量”三个指令,指出重排序后可能变成“分配内存→赋值给instance→初始化对象”,导致另一线程“得到的是一个未初始化完全的对象”,并给出“可以通过volatile禁止指令重排”的解法,因此推出本卡结论。 - 原始内容:编译器、CPU都可能对没有内存屏障、数据依赖关系的操作进行重排序……指令重排之后,获取单例可能导致问题的发生……由于线程B得到的是一个未初始化完全的对象,因此访问instance成员变量的时候可能发生异常。如何确保线程B获取的是一个完成初始化的单例呢?可以通过volatile禁止指令重排。