具有类Reentrant(true(锁的Lock接口的工作方式是使用BlockingQueue来存储想要获取锁的线程。这样一来,"先来先出"的线程——FIFO。一切都清楚了。
但是"不公平的锁"或ReentrantLock(false(会去哪里呢。它们的内部实现是什么?操作系统如何决定现在要选择哪个线程?最重要的是,现在这些线程也存储在队列中吗?(他们一定在某个地方(
类ReentrantLock
不使用BlockingQueue
。它在幕后使用了AbstractQueuedSynchronizer
的非公共子类。
AbstractQueuedSynchronizer
类,正如其文档所述,维护"先进先出(FIFO(等待队列"。这种数据结构对于公平锁和不公平锁是相同的。这种不公平并不意味着锁会改变排队等待线程的顺序,因为这样做没有任何好处。
关键的区别在于,不公平的锁允许lock
尝试在锁刚刚释放时立即成功,即使有其他线程等待锁的时间更长。在这种情况下,队列甚至不涉及超越线程。这比将当前线程添加到队列并将其置于等待状态,同时从队列中删除等待时间最长的线程并将其状态更改为"可运行"更有效。
当锁此时不可用时,线程试图获取它,该线程将被添加到队列中,此时,它的公平锁和不公平锁之间没有区别(除了其他线程可能在不排队的情况下超越它(。由于没有为不公平的锁指定顺序,因此它可以在后台使用后进先出法数据结构,但显然,两者都只有一个实现代码更简单。
另一方面,对于不支持公平获取的synchronized
,有一些JVM实现使用LIFO结构。这可能会从一个版本更改为另一个版本(甚至是相同的版本,这是某些JVM选项或环境方面的副作用(。
在这方面的另一个有趣的点是,ReentrantLock
实现的无参数tryLock()
将是不公平的,即使在锁处于公平模式的情况下也是如此。这表明不公平不是等待队列的属性,而是对进行新锁定尝试的到达线程的处理。
即使此锁已设置为使用公平排序策略,对
tryLock()
的调用也将立即获取可用的锁,无论其他线程当前是否正在等待该锁。这个";驳船运输";行为在某些情况下是有用的,即使它破坏了公平。