“公平”和“不公平”锁之间的内部存储差异

问题描述

具有Reentrant(true)锁类的Lock接口的工作方式是,它使用 BlockingQueue 存储要获取 lock 的线程。这样,“先到先出” -FIFO的线程。一切都清楚了。

但是“不正当锁”在哪里,或者reentrantlock(false)。它们的内部实现是什么? OS如何决定现在选择哪个线程?最重要的是,这些线程现在也存储在队列中还是在哪里? (它们必须在某处)

解决方法

ReentrantLock不使用BlockingQueue。它在幕后使用了AbstractQueuedSynchronizer的非公开子类。

AbstractQueuedSynchronizer类,如其文档所述,维护“先进先出(FIFO)等待队列”。对于公平锁和不公平锁,此数据结构相同。不公平的做法并不意味着锁会改变排队的等待线程的顺序,因为这样做没有任何好处。

关键区别在于,不公平的锁定允许lock尝试在锁定刚刚释放时立即成功执行,即使有其他线程在等待更长的时间。在这种情况下,超线程甚至不涉及该队列。这比将当前线程添加到队列并将其置于等待状态,同时从队列中删除最长的等待线程并将其状态更改为“可运行”更为有效。

当该锁尚不可用时,线程将尝试获取该锁,该线程将被添加到队列中,此时,对其的公平锁和不公平锁之间没有区别(除非其他线程可能超越它而不被排队)。由于尚未为不正确的锁定指定顺序,因此可以在后台使用LIFO数据结构,但是为这两种情况仅使用一个实现代码显然会更简单。

另一方面,对于synchronized,它不支持公平获取,有一些使用LIFO结构的JVM实现。这可能会从一个版本更改为另一个版本(或者甚至具有相同版本,这是某些JVM选项或环境方面的副作用)。

在这方面的另一个有趣的点是,即使在锁定处于公平模式的情况下,ReentrantLock实现的parameterless tryLock()也将是不公平的。这表明不公平不是这里等待队列的属性,而是对进行新锁定尝试的到达线程的处理。

即使将此锁设置为使用公平的订购策略,只要有可用的锁,对tryLock() 的调用都会立即获取,无论当前是否正在等待其他线程锁。即使破坏公平性,这种“讨价还价”的行为在某些情况下还是有用的。