单例模式的双重锁为什么要加volatile?
1 个回答
醉后不知天在水,满船清梦压星河。李后才### 单例模式中的双重检查锁定及 volatile 关键字的使用 在单例模式中,双重检查锁定(Double-Checked Locking)用于减少获取锁的开销,从而提高性能。为了确保线程安全,需要使用 `volatile` 关键字。 #### 双重检查锁定示例 ```java public class Singleton { 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; } } ``` #### 需要 `volatile` 关键字的原因 在并发环境下,如果没有 `volatile` 关键字,`instance = new Singleton();` 可能会出现指令重排序问题。该语句可以分解为三步伪代码: 1. `memory = allocate()` // 分配内存 2. `ctorInstance(memory)` // 初始化对象 3. `instance = memory` // 设置 instance 指向刚分配的内存地址 由于指令重排序,步骤 2 和 3 可能会交换顺序,变为: 1. `memory = allocate()` // 分配内存 2. `instance = memory` // 设置 instance 指向刚分配的内存地址 3. `ctorInstance(memory)` // 初始化对象 在多线程环境下,可能会出现以下问题: 1. 线程 A 执行 `getInstance()` 方法,经过第一层 `if (instance == null)` 判断进入同步块。 2. 线程 A 进入同步块,经过第二层 `if (instance == null)` 判断开始实例化对象。 3. 线程 A 执行 `instance = new Singleton();`,但由于重排序,先执行了步骤 1 和 3。 4. 此时,线程 B 执行 `getInstance()` 方法,经过第一层 `if (instance == null)` 判断发现 `instance` 已不为 `null`(因为步骤 3 已经执行)。 5. 线程 B 返回 `instance`,但此时 `instance` 可能还未初始化完成(因为步骤 2 尚未执行)。 这导致线程 B 获取到了一个未完全初始化的 `Singleton` 实例,可能引发程序错误。 #### `volatile` 关键字的作用 `volatile` 关键字可以防止指令重排序,确保在写入 `instance` 时,初始化操作已经完成。具体而言: - `volatile` 保证了变量的可见性,任何线程对变量的写操作对其他线程都是立即可见的。 - 禁止指令重排序,确保对象初始化过程的顺序不会被打乱。 通过在 `instance` 变量前添加 `volatile` 关键字,可以有效避免上述重排序问题,保证双重检查锁定的正确性。编辑于 2023-03-14 12:48 •IP属地 北京举报评论收藏
公众号进入网站体验更佳

