并发编程原理与实战(十八)ReentrantLock API全面解析

上一篇讲解了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(),但更灵活
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帧栈

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值