ReentrantLock的ConditionObject字段为何未用volatile修饰?
关于ConditionObject中firstWaiter/lastWaiter无volatile修饰的内存一致性疑问
已知ConditionObject的firstWaiter和lastWaiter字段未被volatile修饰,但只有持有独占锁的线程才能修改这两个字段。这里有个疑问:如果一个线程修改这两个字段后,另一个线程读取时会不会因为CPU缓存没拿到最新值导致不一致?为什么不需要volatile就能保证内存一致性?
对应的代码实现:
public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; /** 这两个字段未被volatile修饰,可能被不同线程修改 */ private transient ConditionNode firstWaiter; private transient ConditionNode lastWaiter; }
核心答案:独占锁的内存语义已经兜底
其实根本原因在于,所有对这两个字段的读写操作,都必须在持有独占锁的前提下执行,锁的获取与释放本身就自带了内存屏障的效果,完全覆盖了volatile的作用:
- 当线程释放独占锁时,JMM会强制把该线程本地缓存里的所有修改(包括对
firstWaiter/lastWaiter的修改)刷新到主内存中,确保修改对外可见。 - 当另一个线程获取同一把独占锁时,JMM会让该线程的本地缓存直接失效,必须从主内存重新读取所有共享变量的最新值,自然也能拿到这两个字段的最新状态。
再结合Condition的实际运行逻辑看:
- 调用
await方法时,线程会先释放锁,进入Condition的等待队列(此时修改firstWaiter/lastWaiter的操作是在持有锁时完成的);被signal唤醒后,线程又会重新竞争获取锁,之后才能访问等待队列的节点。 - 调用
signal/signalAll方法时,只有持有锁的线程才能执行,修改等待队列节点的操作同样在锁的临界区内完成。
简单说,这两个字段的读写完全被锁的临界区包裹,锁的内存语义已经保证了修改的可见性和有序性,根本不需要额外加volatile。
内容的提问来源于stack exchange,提问作者Machi
相关产品推荐
相关产品推荐

