我认为 JavaDoc 中关于 AbstractQueuedSynchronizer 的两句话不清楚/具有误导性,因此我想澄清这些问题。
第一句话说:
尽管此类基于内部 FIFO 队列,但它不会自动强制执行 FIFO 获取策略。
上面这句话到底是什么意思?查找类本身的源代码(其中有几个标记为
final
的方法),我目前只看到一种无法强制执行 FIFO 的情况,即当 tryAcquire
返回 true
时。当 tryAcquire
返回 false
并且调用者线程必须暂停时,我找不到任何绕过 FIFO 的方法。如果我的假设是正确的,文档不应该更清楚吗?
第二句说:
因为获取中的检查是在排队之前调用的,所以新获取的线程可能会抢在其他被阻塞和排队的线程之前。但是,如果需要,您可以定义 tryAcquire 和/或 tryAcquireShared 以通过内部调用一个或多个检查方法来禁用插入,从而提供公平的 FIFO 获取顺序。
这里的barging到底是什么意思?文档是不是又在讲获取线程没有停放的情况了?
我目前的理解是这样的:只要获取线程没有parked,除了FIFO以外的策略都是可以的。如果获取线程必须停放,则将使用 FIFO。
您同意我的说法还是我理解错了?开个BUG让Oracle来修复有意义吗?
查看
ReentrantLock
的源代码,看看它在实践中是如何实现的。NonfairSync
:
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
FairSync
:
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && !hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
如您所见,
FairSync.tryAcquire()
还会检查 !hasQueuedPredecessors()
,正如 AbstractQueuedSynchronizer
的 javadoc 建议的那样:
因为获取中的检查是在排队之前调用的,所以新获取的线程可能会在其他被阻塞和排队的线程之前“插入”。但是,如果需要,您可以定义
tryAcquire
和/或以通过内部调用一个或多个检查方法来禁用插入,从而提供tryAcquireShared
fairFIFO 采集顺序。特别是,如果tryAcquire
(专门设计用于公平同步器使用的方法)返回,则大多数公平同步器可以定义false
来返回hasQueuedPredecessors()
。其他变化也是可能的。 简单来说,以下案例说明了其中的差异:true
当前锁所有者释放锁,并且在释放期间,从 FIFO 队列中等待时间最长的线程被释放。
FairSync
NonfairSync