StampedLock 如何对锁请求进行排队?

Posted

技术标签:

【中文标题】StampedLock 如何对锁请求进行排队?【英文标题】:How does StampedLock queue lock requests? 【发布时间】:2018-10-13 19:13:44 【问题描述】:

我正在研究基于 Java8 的 StampedLock (javadoc here) 锁定缓存,但尽管阅读了 StampedLock Idioms 之类的文章,但我在网上找不到令人信服的实现。

在对ReentrantReadWriteLock 不允许将读锁升级为写锁感到震惊之后,我对 Java 的多线程和并发产品感觉不太乐观,随后又难以找到一个有信誉的替代解决方案.

我的问题是,没有明确的声明可以减轻我对StampedLock 会在有读取请求排队时无限期阻止写入请求的担忧。

查看文档,有 2 个 cmets 引起了我的怀疑。

来自Javadoc:

StampedLock 的调度策略并不一致 读者超过作者,反之亦然。所有的“尝试”方法都是尽力而为 并且不一定符合任何调度或公平政策。

来自source code:

 * These rules apply to threads actually queued. All tryLock forms
 * opportunistically try to acquire locks regardless of preference
 * rules, and so may "barge" their way in.  Randomized spinning is
 * used in the acquire methods to reduce (increasingly expensive)
 * context switching while also ....

所以它暗示了一个读写锁队列,但我需要阅读和消化整个 1500 行源代码才能确定它。

我认为它一定存在,因为我找到了 good benchmarking article,它表明 StampedLock 是进行多次读取/少量写入的方式。但是,由于缺乏在线报道,我仍然很担心。

从根本上说,我想我希望有一个实现,我可以在 javadoc 之后即插即用,但最后我还是在网上扎根,想知道为什么在任何地方都没有循环 StampedLock#tryOptimisticRead() 的示例 -即使code from the benchmark article 也不这样做。

Java 并发这么难还是我遗漏了一些明显的东西?

【问题讨论】:

【参考方案1】:

“Java 并发这么难还是我遗漏了一些明显的东西?”

Java 并发是否比(例如)C++ 或 Python 并发更难,这是一个见仁见智的问题。

但是,是的,在任何允许不同线程直接更新共享数据结构的语言中,并发都是困难的2。 (仅)支持类似 CSP 的并发的语言更容易理解和推理。

参考:

https://en.wikipedia.org/wiki/Communicating_sequential_processes

就您关于公平的观点而言,Java 中的大多数锁定形式确实不能保证公平。事实上,与线程调度有关的许多事情(故意)松散地指定。但是编写避免这些问题的代码并不难……一旦您了解了这些库以及如何使用它们。


关于StampedLock 行为的具体问题。

我的问题是,没有明确的声明可以减轻我对StampedLock 会在有读取请求排队时无限期阻止写入请求的担忧。

没有这样的声明3 因为这样的行为是可能的。它源自对StampedLock API 文档的仔细阅读。例如,它说:

StampedLock 的调度策略并不总是优先选择读者而不是作者,反之亦然。”

简而言之,没有什么可以保证不定时的writeLock 最终会获得锁。

如果您需要断然避免读者导致写者饿死的情况,那么不要使用writeLockreadLock。您可以使用tryOptimisticRead 而不是readLock。或者您可以设计和实现不同的同步机制。


最后,您似乎暗示StampedLock 应该提供一种直接处理您的场景的方法和/或该文档应该专门向非专家用户解释如何处理它.我提请人们注意这一点:

StampedLocks 被设计用作开发线程安全组件的内部实用程序。”。

您很难找到相关示例,这不是 javadocs 的错。如果有的话,它支持这个 API 是为专家提供的推断......


1 - 我认为 Java 的并发支持至少比大多数其他同类语言更容易推理。 Java 内存模型(JLS 的第 17.4 章)已详细说明,Goetz 等人的“Java Concurrency In Practice”很好地解释了并发编程的来龙去脉。 2 - .... 适用于大多数程序员。 3 - 如果这对您来说不够明确,请给自己写一个示例,其中存在(模拟的)无限大序列的读取请求和多个读取器线程。运行它并查看写入器线程是否停止,直到读取请求全部耗尽。

【讨论】:

以上是关于StampedLock 如何对锁请求进行排队?的主要内容,如果未能解决你的问题,请参考以下文章

Glide 是不是对每个图像请求进行排队?滚动时Recyclerview加载非常慢

开始 fetch,在 fetch 期间对中间请求进行排队,然后为所有人提供数据

如何取消所有排队的请求onFailure? (改造 2)

公平锁与非公平锁

由于每秒上限而限制和排队 API 请求

Java虚拟机对锁优化所做的努力(读书笔记)