Lock接口及其实现类:Java 并发编程的灵活锁机制
Lock 接口是 JDK 5 引入的同步工具,为 Java 并发编程提供了比 synchronized 更灵活的锁控制能力。它支持显式加锁 / 解锁、可中断获取、超时获取等特性,弥补了 synchronized 隐式操作的局限性。本文将深入解析 Lock 接口的核心实现类(ReentrantLock 和 ReentrantReadWriteLock),并对比其与 synchronized 的差异。
Lock 接口核心方法
Lock 接口定义了锁的基本操作,核心方法如下:
| 方法 | 作用描述 |
|---|---|
lock() |
获取锁(阻塞式,直到获取成功) |
lockInterruptibly() |
可中断地获取锁(获取过程中若线程被中断,会抛出 InterruptedException) |
tryLock() |
尝试非阻塞获取锁(成功返回 true,失败返回 false) |
tryLock(long timeout, TimeUnit unit) |
超时获取锁(超时未获取则返回 false) |
unlock() |
释放锁(必须在 finally 块中调用,避免锁泄漏) |
newCondition() |
创建条件对象,实现线程间通信(类似 synchronized 的 wait/notify) |
ReentrantLock:可重入独占锁
ReentrantLock 是 Lock 接口的最常用实现,提供与 synchronized 类似的独占锁功能,但支持更灵活的控制。其核心特性是可重入性(同一线程可多次获取锁)和公平 / 非公平模式。
公平锁与非公平锁
ReentrantLock 允许通过构造函数指定锁的公平性:
1 | // 非公平锁(默认) |
(1)非公平锁(NonfairSync)
- 获取逻辑:线程尝试直接获取锁(CAS 操作),失败后才进入等待队列;
- 特点:可能存在线程饥饿(先请求的线程后获取锁),但吞吐量更高;
- 适用场景:并发激烈但允许偶尔不公平的场景。
(2)公平锁(FairSync)
- 获取逻辑:线程必须按请求顺序进入队列,只有队首线程能获取锁;
- 特点:保证公平性,避免饥饿,但频繁的上下文切换导致吞吐量较低;
- 适用场景:对公平性要求高的场景(如资源调度)。
核心源码解析
ReentrantLock 基于 AQS(AbstractQueuedSynchronizer)实现,内部通过 Sync 抽象类区分公平与非公平逻辑:
