上一篇讲解了Lock接口核心API和相比于synchronized的关键优势,本文来进一步学习Lock接口的具体实现类ReentrantLock。
认识ReentrantLock
基本行为和语义
下面我们先看ReentrantLock的定义。
/**
* A reentrant mutual exclusion {@link Lock} with the same basic
* behavior and semantics as the implicit monitor lock accessed using
* {@code synchronized} methods and statements, but with extended
* capabilities.
*/
ReentrantLock是一种可重入的互斥锁Lock,其基本行为和语义与通过synchronized方法及语句访问的隐式监视器锁相同,但具备扩展功能。
那么synchronized有哪些基本行为和语义呢?通过前面文章的学习,我们已经知道synchronized具有上锁、解锁、互斥访问等行为,这些就是基本行为。synchronized修饰的方法或语句块所有操作作为一个不可分割的整体执行,也就是具有原子性;线程在释放锁之前会将共享变量的修改刷新到主内存,其他线程在获取锁后能立即看到最新值,也就是可见性;同一线程可以多次获取同一把锁,也就是可重入性。
/* <p>A {@code ReentrantLock} is <em>owned</em> by the thread last
* successfully locking, but not yet unlocking it. A thread invoking
* {@code lock} will return, successfully acquiring the lock, when
* the lock is not owned by another thread. The method will return
* immediately if the current thread already owns the lock. This can
* be checked using methods {@link #isHeldByCurrentThread}, and {@link
* #getHoldCount}.
*/
ReentrantLock的所有权归属于最后一个成功加锁但尚未解锁的线程。当锁未被其他线程持有时,调用lock()方法的线程将成功获取锁并返回。若当前线程已持有该锁,则此方法立即返回。可通过方法 isHeldByCurrentThread和 getHoldCount验证锁状态。
/* <p>The constructor for this class accepts an optional
* <em>fairness</em> parameter. When set {@code true}, under
* contention, locks favor granting access to the longest-waiting
* thread. Otherwise this lock does not guarantee any particular
* access order. Programs using fair locks accessed by many threads
* may display lower overall throughput (i.e., are slower; often much
* slower) than those using the default setting, but have smaller
* variances in times to obtain locks and guarantee lack of
* starvation. Note however, that fairness of locks does not guarantee
* fairness of thread scheduling. Thus, one of many threads using a
* fair lock may obtain it multiple times in succession while other
* active threads are not progressing and not currently holding the
* lock.
* Also note that the untimed {@link #tryLock()} method does not
* honor the fairness setting. It will succeed if the lock
* is available even if other threads are waiting.
*/
公平性设置
该类的构造函数接受一个可选的公平性参数,当设置为true时,在竞争条件下,锁优先授予等待时间最长的线程。否则,此锁不保证任何特定访问顺序。使用公平锁的多线程程序可能比默认设置(非公平锁)的整体吞吐量更低(即更慢,通常显著更慢), 但获取锁的时间波动更小,且能避免线程饥饿。需注意:锁的公平性并不保证线程调度的公平性。因此,当其他活跃线程未持有锁且未推进时,使用公平锁的某个线程可能连续多次获得锁。
另请注意:非超时的tryLock()方法不遵循公平性设置,只要锁可用,即使其他线程正在等待,它也会成功。
推荐用法
强烈建议在调用 lock()方法后立即使用 try 代码块,典型结构如下所示:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // 阻塞直至获取锁
try {
// ... 方法逻辑
} finally {
lock.unlock(); // 确保释放锁
}
}
}
其他特性
/* <p>In addition to implementing the {@link Lock} interface, this
* class defines a number of {@code public} and {@code protected}
* methods for inspecting the state of the lock. Some of these
* methods are only useful for instrumentation and monitoring.
*
* <p>Serialization of this class behaves in the same way as built-in
* locks: a deserialized lock is in the unlocked state, regardless of
* its state when serialized.
*
* <p>This lock supports a maximum of 2147483647 recursive locks by
* the same thread. Attempts to exceed this limit result in
* {@link Error} throws from locking methods.
*/
除了实现Lock接口的方法外,此类还定义了若干用于检查锁状态的, public和protected 方法。其中部分方法仅适用于监控工具和状态跟踪。
该类的序列化(将 Java 对象转换为字节流(byte[]),用于保存到文件、数据库或网络传输等)行为与内置锁一致:无论序列化时的状态如何,反序列化(将字节流恢复为原始对象状态的过程)后的锁均处于未锁定状态。此锁支持同一线程最多递归加锁 2147483647 次(2147483647 层嵌套锁定)。尝试超出该限制将导致加锁方法抛出错误。
API详解
ReentrantLock除了实现Lock接口的6个基础方法外,还实现了其他方法,下面我们主要对其他方法进行分析。
公平性控制
通过构造函数创建ReentrantLock实例时指定参数来控制公平性。
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
该构造函数创建一个ReentrantLock类型的实例,相当于使用构造函数ReentrantLock(false)创建实例。
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
该构造函数根据给定的公平策略创建一个ReentrantLock实例。参数fair为true时,锁将使用公平的排队抢锁策略。
状态查询与监控
(1)getHoldCount()
/**
* Queries the number of holds on this lock by the current thread.
*
* <p>A thread has a hold on a lock for each lock action that is not
* matched by an unlock action.
*
* <p>The hold count information is typically only used for testing and
* debugging purposes. For example, if a certain section of code should
* not be entered with the lock already held then we can assert that
* fact:
...
*
* @return the number of holds on this lock by the current thread,
* or zero if this lock is not held by the current thread
*/
public int getHoldCount() {
return sync.getHoldCount();
}
该方法查询当前线程对此锁的持有次数。线程每次未匹配解锁操作的锁操作都会持有该锁。持有计数信息通常仅用于测试和调试目的。例如,如果某个代码段不应在锁已被持有时进入,则我们可以断言这一事实:
class X {
final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
assert lock.getHoldCount() == 0;
lock.lock();
try {
// ... method body
} finally {
lock.unlock();
}
}
该方法返回当前线程持有该锁的次数,或者返回0如果当前线程没有持有该锁。
(2)isHeldByCurrentThread()
/**
* Queries if this lock is held by the current thread.
*
* <p>Analogous to the {@link Thread#holdsLock(Object)} method for
* built-in monitor locks, this method is typically used for
* debugging and testing. For example, a method that should only be
* called while a lock is held can assert that this is the case:
...
* @return {@code true} if current thread holds this lock and
* {@code false} otherwise
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
该方法查询当前线程是否持有该锁,该方法类似于内置监视器锁的Thread类的holdsLock(Object)方法,通常用于调试和测试。例如,若某方法仅应在持有锁时调用,可通过此方法断言当前状态:
class X {
final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
assert lock.isHeldByCurrentThread();
// ... method body
}
}
该断言也可用于确保以非重入方式使用可重入锁,例如:
class X {
final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
assert !lock.isHeldByCurrentThread();
lock.lock();
try {
// ... method body
} finally {
lock.unlock();
}
}
}
(3)isLocked()
/**
* Queries if this lock is held by any thread. This method is
* designed for use in monitoring of the system state,
* not for synchronization control.
*
* @return {@code true} if any thread holds this lock and
* {@code false} otherwise
*/
public boolean isLocked() {
return sync.isLocked();
}
该方法查询这个锁是否被当前线程持有,该方法用于监控系统的状态,而不是用来做同步控制。如果当前线程持有该锁将返回true,否则返回false。
(4)isFair()
/**
* Returns {@code true} if this lock has fairness set true.
*
* @return {@code true} if this lock has fairness set true
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
该方法返回这个锁是否设置了公平性,如果设置了则返回true。
(5)hasQueuedThreads()
/**
* Queries whether any threads are waiting to acquire this lock. Note that
* because cancellations may occur at any time, a {@code true}
* return does not guarantee that any other thread will ever
* acquire this lock. This method is designed primarily for use in
* monitoring of the system state.
*
* @return {@code true} if there may be other threads waiting to
* acquire the lock
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
该方法 查询是否有线程正在等待获取此锁。需注意,由于线程取消可能随时发生,返回true并不保证其他线程最终会获得此锁。此方法主要用于监控系统状态。如果有线程正在等待获取锁,则返回true。
hasQueuedThread(Thread thread)则是查询给定的线程是否正在等待获取此锁。
(6)getQueueLength()
/**
* Returns an estimate of the number of threads waiting to acquire
* this lock. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
* monitoring system state, not for synchronization control.
*
* @return the estimated number of threads waiting for this lock
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
该方法返回等待获取此锁的线程数估计值。该值仅为估算,因为此方法遍历内部数据结构时,线程数量可能动态变化。此方法设计用于监控系统状态,而非用于同步控制。
/**
* Queries whether any threads are waiting on the given condition
* associated with this lock. Note that because timeouts and
* interrupts may occur at any time, a {@code true} return does
* not guarantee that a future {@code signal} will awaken any
* threads. This method is designed primarily for use in
* monitoring of the system state.
*
* @param condition the condition
* @return {@code true} if there are any waiting threads
* @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this lock
* @throws NullPointerException if the condition is null
*/
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
该方法查询是否有线程正在等待与此锁关联的给定条件。需注意,由于超时和中断可能随时发生,返回 true并不保证未来的 signal 会唤醒任何线程。此方法主要用于监控系统状态。
- @param condition 条件
- @return 如果有等待线程存在则返回true
- @throws IllegalMonitorStateException 如果线程未持有此锁
- @throws IllegalArgumentException 如果给定条件与此锁不关联
- @throws NullPointerException 如果给定的条件为null
int getWaitQueueLength(Condition condition)方法则是返回正在等待与此锁关联的给定条件的线程数量。
总结
ReentrantLock 方法总览
分类 | 方法 | 功能描述 | 注意事项 |
---|---|---|---|
基础锁操作 | void lock() | 阻塞式获取锁,支持重入(同一线程可多次加锁) | 必须在 finally 块中调用 unlock() 避免死锁 |
基础锁操作 | void unlock() | 释放锁,重入时需调用次数与 lock() 匹配 | 非锁持有者调用会抛出 IllegalMonitorStateException |
基础锁操作 | boolean tryLock() | 尝试非阻塞获取锁,成功返回 true | 适用于快速失败逻辑(如避免死锁检测) |
基础锁操作 | boolean tryLock(long, TimeUnit) | 在指定时间内尝试获取锁,支持中断 | 超时或中断返回 false ,需处理 InterruptedException |
基础锁操作 | void lockInterruptibly() | 可中断式获取锁,阻塞期间响应中断 | 需捕获 InterruptedException 并恢复中断状态 |
状态查询 | boolean isLocked() | 检查锁是否被任何线程持有(包括重入状态) | 结果仅为瞬时状态,可能因并发变化 |
状态查询 | boolean isHeldByCurrentThread() | 当前线程是否持有锁 | 用于调试或条件判断 |
状态查询 | int getHoldCount() | 返回当前线程的重入次数 | 计数器归零时锁完全释放 |
队列监控 | int getQueueLength() | 返回等待锁的线程数(估算值) | 高并发场景下结果可能不精确 |
队列监控 | boolean hasQueuedThreads() | 检查是否有线程在等待队列中 | 通常用于性能监控 |
公平性控制 | ReentrantLock(boolean fair) | 构造方法:true 为公平锁(按队列顺序),false 为非公平锁(默认) | 公平锁性能较低,非公平锁吞吐量更高 |
条件变量 | Condition newCondition() | 创建与锁绑定的 Condition 对象,用于线程间通信 | 类似 Object.wait() /notify() ,但更灵活 |