"公平锁"和"非公平锁"之间的内部存储区别

5

ReentrantLock(true)使用 BlockingQueue 来存储想要获取的线程,以FIFO方式处理。关于这一点大家都很清晰。

那么对于 'unfair locks' 或 ReentrantLock(false),它们的内部实现在哪里呢?操作系统如何决定选取哪个线程?最重要的是,现在这些线程也被存储在一个队列中吗?(它们必须在某个地方)


“队列”本身就在操作系统中,通常用于存储被阻塞的线程。操作系统并没有指定队列算法(或者至少通常不会这样做,因此Java作为可移植性语言必须假设最坏情况),因此Java假设它是不公平的。 - markspace
1个回答

9

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

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

不公平锁的关键区别在于,当锁刚被释放时,一个不公平的锁允许lock尝试立即成功,即使有其他线程等待更长时间的锁。在这种情况下,队列甚至不涉及超车线程。这比将当前线程添加到队列中并将其置于等待状态,同时从队列中删除最长等待线程并将其状态更改为“可运行”更有效。
当锁在尝试获取它的线程时不可用时,该线程将被添加到队列中,并且此时对于它(除了其他线程可能会超越它而不排队),公平和不公平锁之间没有区别。由于未指定不公平锁的顺序,因此它可以在幕后使用LIFO数据结构,但显然为两者编写一个实现代码更简单。
另一方面,对于不支持公平获取的synchronized,某些JVM实现使用LIFO结构。这可能会随版本不同而更改(甚至在相同版本中,作为某些JVM选项或环境方面的副作用)。
在这方面,另一个有趣的点是,即使锁已经设置为使用公平的排序策略,ReentrantLock实现中的无参 tryLock()也将是不公平的。这表明,不公平不是等待队列的属性,而是到达线程尝试获取新锁的处理方式。

即使此锁已被设置为使用公平的排序策略,对tryLock()的调用立即获取锁(是否有其他线程正在等待锁),这种“插队”行为在某些情况下可能很有用,尽管它破坏了公平性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接