Java并发中volatile何时必须使用?示例Result类value是否需volatile修饰?
Java多线程volatile相关问题解答
1. Java开发中必须为变量添加volatile关键字修饰的场景
volatile关键字的核心作用是保证变量的线程可见性、禁止指令重排序,但不保证操作的原子性,以下场景必须使用volatile修饰:
- 多线程共享的状态标记量:比如用于控制线程启停的
isShutdown这类布尔标记,这类变量通常只会被一个线程赋值、其他线程只读,没有锁交互,不加volatile会导致修改不能及时被其他线程感知,线程无法按照预期停止。 - 双重检查锁(DCL)实现的单例模式:如果单例变量不加volatile,指令重排序可能会把对象初始化的指令重排到赋值操作之后,导致其他线程拿到还没完成初始化的半实例对象,引发空指针等异常。
- 读多写少的低开销同步场景:如果变量的写操作本身是原子操作(比如直接给基础类型/引用赋值,不是
i++这类复合操作),且读操作频率远高于写,用volatile替代锁可以大幅降低同步开销,同时保证读操作总能拿到最新值。
2. 示例代码中Result类的value变量不需要加volatile修饰
原因如下:
- 首先Java内存模型对
synchronized有明确的happens-before规则:线程解锁前对变量的修改,会全部同步回主内存;线程加锁后读取变量,会直接从主内存拉取最新值,保证可见性。代码中对value的所有写操作、以及同步块内的二次空校验读操作,都被synchronized(result)块包裹,已经由同步锁保证了可见性和有序性。 - 你可能会注意到Task1、Task2中第一个
if (result.getValue() == null)判断是在同步块外执行的,这个场景就算读到了本地缓存的旧值也不会影响正确性:就算首次判断误读到null,进入同步块后还有第二次空校验,第二次校验一定能拿到主内存的最新值,最多只是多执行了一次获取锁的逻辑,不会出现并发安全问题。 - 主线程最后调用
result.getValue()之前,已经执行完包含wait()的同步块,wait()返回时会重新获取锁,退出同步块时已经同步了主内存的最新值,所以也不会读到旧值。
内容的提问来源于stack exchange,提问作者Allme
相关产品推荐
相关产品推荐

