如何声明 CopyOnWriteArrayList 的锁?
在 CopyOnWriteArrayList 的源码中,lock 字段的声明包含 final 和 transient 两个关键字。
final transient ReentrantLock lock = new ReentrantLock();
1. final 的作用
-
不可变性:
final表示lock引用一旦初始化后不能再指向其他对象(即不能重新赋值),保证引用在对象生命周期内不可变。。- 确保锁对象的唯一性,防止多线程环境下因锁被意外替换而导致并发问题。
-
线程安全:
final保证锁对象的初始化对其他线程可见(遵循 JMM 的happens-before规则)。- 避免因指令重排序导致的未初始化锁被误用。
2. transient 的作用
- 序列化排除:
transient表示该字段不会被默认的序列化机制保存。- 原因:
ReentrantLock是线程同步的内部状态工具,序列化锁对象无意义,且可能破坏线程安全性。 - 反序列化时会重新创建一个新的锁对象(通过
readObject方法初始化)。
3. 结合设计意图的分析
-
final的必要性CopyOnWriteArrayList依赖唯一的lock控制所有写操作的互斥性,若允许修改锁引用,会导致线程失去同步保障。- 示例:若锁被替换,两个线程可能分别使用不同的锁,失去互斥效果。
-
transient的必要性- 锁对象的状态(如持有锁的线程、重入次数)是运行时动态的,序列化这些状态无意义。
- 反序列化后必须生成新锁,以保持线程安全语义的一致性。
4. 对比其他场景
final的典型用途:- 常量、不可变对象(如
String)、线程共享的引用(如本例中的锁)。
- 常量、不可变对象(如
transient的典型用途:- 敏感数据(如密码)、运行时动态状态(如锁、线程池)、冗余或可重建的数据。
5. 反序列化时的处理
在 CopyOnWriteArrayList 的 readObject 方法中会重新初始化锁(伪代码):
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
resetLock(); // 重新初始化锁
// ...
}
private void resetLock() {
NSAFE.putObjectVolatile(this, lockOffset, new ReentrantLock());
}
对序列化和反序列化感兴趣的可参考上篇笔记。CopyOnWriteArrayList 实现了 Serializable 接口,因此可以通过 readObject 方法来控制反序列化流程。
总结
| 关键字 | 作用 | 设计意图 |
|---|---|---|
final |
确保锁引用不可变,保障线程安全 | 防止锁对象被意外替换,破坏同步机制。 |
transient |
禁止锁对象序列化,反序列化时重建 | 锁是运行时状态,序列化无意义且不安全。 |
这两个关键字共同确保了 CopyOnWriteArrayList 在并发和序列化场景下的正确性。

浙公网安备 33010602011771号