【问题标题】:Difference in internal storing between 'fair' and 'unfair' lock“公平”和“不公平”锁的内部存储差异
【发布时间】:2020-12-05 10:15:29
【问题描述】:

带有类 Reentrant(true) 锁的 Lock 接口的工作方式是它使用 BlockingQueue 来存储想要获取 的线程。以这种方式“先来,先出去”的线程 - FIFO。一切都清楚。

但是“不公平的锁”去哪里了,或者 ReentrantLock(false)。他们的内部实现是什么?操作系统如何决定现在选择哪个线程?最重要的是现在这些线程也存储在队列中还是在哪里? (它们一定在某个地方)

【问题讨论】:

  • “队列”在操作系统本身,它通常存储被阻塞的线程。操作系统没有指定队列算法是什么(或者至少它通常不会,因此可移植的 Java 必须假设最坏的情况),因此 Java 假设它是不公平的。

标签: java locking reentrantlock


【解决方案1】:

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

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

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

当锁不可用时,一个线程尝试获取它,线程将被添加到队列中,此时,公平锁和不公平锁没有区别(除了其他线程可能超越它而不被排队)。由于没有为不公平锁指定顺序,它可以在幕后使用 LIFO 数据结构,但对于两者只有一个实现代码显然更简单。

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

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

即使此锁已设置为使用公平排序策略,对 tryLock() 的调用 将立即获取该锁(如果可用),无论其他线程当前是否正在等待锁。这种“闯入”行为在某些情况下可能很有用,即使它破坏了公平性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多