可重入锁条件公平

Reentrant lock condition fairness

我对 ReentrantLockCondition 感到困惑。这是文档:

  • Waiting threads are signalled in FIFO order.

  • The ordering of lock reacquisition for threads returning from waiting methods is the same as for threads initially acquiring the lock, which is in the default case not specified, but for fair locks favors those threads that have been waiting the longest.

根据最新的项目符号,公平性在信令上带来了一个明确的锁重新获取顺序。

但是第一个项目符号的含义是什么等待线程按 FIFO 顺序发出信号?我假设在这种情况下,发信号意味着只是“发信号”,这意味着它按照 FIFO 顺序“取消停放”线程,但唤醒时的实际重新请求顺序由公平性决定。

There are pretty large amount of staff 与 HotSpot 内部的 cxq 和等待队列相关联,我不太了解(不幸的是)。


问题:

等待线程是否按 FIFO 顺序发出信号 是否意味着等待线程按照它们被停放的相同顺序被停放(即使锁本身是不公平的)?

公平性是否提供了必要的重新获取顺序保证,因为一般情况下存在 unpark-require race?

阅读ReentrantLock的源码(Java12)可以看出公平和不公平的ReentrantLock只有很小的区别。区别在于扩展 java.util.concurrent.locks.AbstractQueuedSynchronizer 的 class。在一种情况下是 FairSync,在另一种情况下是 NonfairSync。两者都在 ReentrantLock 中定义,唯一的区别是 FairSync 在方法 tryAcquire 中多实现了一项检查。 阅读代码似乎在最佳条件下,在非公平 ReentrantLock FIFO 中也受到尊重,但由于取消、超时或类似情况,这不能保证。在公平的 ReentrantLock 中,任何线程在获取锁之前(如果从队列中解开)重新检查是否有旧线程。 我不确定是否理解第二个问题,但请注意释放锁的线程将线程从队列中解开。此外,如果释放锁的线程取消停放队列中的旧线程,这不足以避免 starvation,因为第三个线程可能需要在退出线程取消停放等待的线程之前同时获得锁。在公平模式下,每次有新线程尝试获得锁时都会检查所有等待的线程,这会授予 FIFO 并避免饥饿。

等待线程的外部中断不会改变队列顺序。

所述,“公平”和“不公平”的实际区别不在于队列的组织方式,而是在非公平模式下,尝试获取锁的线程即使在队列中已经有等待线程。这样的超车线程根本不会和队列交互

Condition 上调用 await 方法之一的线程必须已经拥有关联的锁并将释放它,以便另一个线程可以获得它,满足条件并调用 signalsignalAll。所以该线程必须自己入队,以便其他线程知道向哪个线程发送信号。调用signal时,从FIFO中取出等待条件时间最长的线程。

已发出信号的线程可能未停放,但也可能尚未停放。在任何一种情况下,它都必须重新获取锁,并且此重新获取受锁的公平性保证的约束。当线程调用 signal 时,它必须拥有锁。因此,有信号的线程不能立即成功。释放锁时,可能会出现多线程之间的竞争。

但是条件的 FIFO 顺序信号意味着当两个或多个线程在同一条件下等待并且一个线程收到信号时,它将是等待时间最长的线程,其他线程中的 none 可以超越,即使是不公平的锁。只有当有多个线程被信号通知或其他线程没有等待条件时,才尝试获取锁,非公平锁的获取顺序是任意的。此外,正如链接的答案所提到的,tryLock() 即使在公平锁上也可能超车。