📒 ReentrantLock 用法与源码剖析笔记
🚀 一、ReentrantLock 核心特性
- 🔄 可重入性:同一线程可重复获取锁(最大递归次数为
Integer.MAX_VALUE
) - 🔧 公平性:支持公平锁(按等待顺序获取)和非公平锁(默认,允许插队)
- ⏰ 超时机制:
tryLock(long timeout, TimeUnit unit)
- 🚫 可中断:
lockInterruptibly()
允许响应中断 - 🔗 条件变量:
Condition
实现精准线程唤醒(对比Object.wait/notify
)
🛠️ 二、基础用法模板
ReentrantLock lock = new ReentrantLock();
// 非公平锁(默认) vs 公平锁(new ReentrantLock(true))lock.lock(); // 📌 阻塞获取锁
try {// 临界区代码
} finally {lock.unlock(); // ⚠️ 必须放在 finally 块!
}// 高级用法示例
if (lock.tryLock(1, TimeUnit.SECONDS)) { // ⏳ 带超时尝试try {// ...} finally {lock.unlock();}
}
🔍 三、源码架构分析
-
Sync 同步器(继承 AQS)
- NonfairSync(非公平锁实现)
- FairSync(公平锁实现)
-
AQS 核心机制
- state 字段:锁状态计数器(0=未锁定,>0=锁定次数)
- CLH 队列:线程等待队列(双向链表实现)
⚙️ 四、关键方法源码解析
🔑 1. lock() 方法对比
// 非公平锁实现
final void lock() {if (compareAndSetState(0, 1)) // 🚀 直接尝试插队setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);
}// 公平锁实现
final void lock() {acquire(1); // ⚖️ 必须排队
}// AQS 核心方法
public final void acquire(int arg) {if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}
🔄 2. tryAcquire 差异
// 非公平锁 tryAcquire
protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires); // 🎲 允许插队
}// 公平锁 tryAcquire
protected final boolean tryAcquire(int acquires) {if (getQueueLength() > 0 && getExclusiveOwnerThread() != Thread.currentThread()) {return false; // 🚧 队列有等待线程时禁止获取}// ...后续与非公平锁相同
}
💡 五、设计亮点与注意事项
- � 性能取舍:非公平锁吞吐量更高(减少线程切换),但可能产生线程饥饿
- � 锁释放必须:unlock() 必须执行(建议用 try-finally 包裹)
- 🧵 Condition 高级用法:实现多条件等待(典型应用:生产者-消费者模型)
- ⚠️ 避免死锁:加锁顺序要一致,超时机制可作为兜底
📊 六、与 synchronized 对比
特性 | ReentrantLock | synchronized |
---|---|---|
实现机制 | API 层面 | JVM 内置 |
锁释放 | 必须显式 unlock() | 自动释放 |
公平性 | 可配置 | 非公平 |
中断响应 | 支持 | 不支持 |
条件变量 | 多 Condition | 单 Object monitor |
性能 | 高竞争时更优 | 优化后差距缩小 |
🌟 七、最佳实践建议
- 🆚 优先选择:需要高级功能时用 ReentrantLock,简单场景用 synchronized
- 🧪 锁测试:用 ThreadMXBean 检测死锁
- 📏 锁粒度:尽量缩小锁作用域
- 🧮 性能监控:关注
getQueueLength()
等统计方法