0

根据有关“线程饥饿”的Oracle 文档,

饥饿描述了线程无法定期访问共享资源并且无法取得进展的情况。当共享资源被“贪婪”线程长时间不可用时,就会发生这种情况。

在关于线程“线程争用”的Oracle 文档中,

如果一个线程试图获取一个已经被另一个线程持有的锁,那么它必须等到锁被释放。发生这种情况时,就会出现所谓的锁“争用”。

其实,上面的定义是有一定道理的,我无法得到一个明确的定义来对比它们。谁能解释一下上述术语之间的区别以及它们之间的关系(如果有的话)?

注意:通过参考什么是线程争用的一些答案 ,我觉得这些答案也不适用于“线程饥饿”吗?

4

1 回答 1

3

“饥饿”是指一种特定形式的争用,其中一个(或几个)特定线程比其他线程更频繁地被锁定。当线程以某种方式交互时会发生这种情况,以至于每次“饥饿”线程尝试获取某个锁时,它总是在某个其他线程已经锁定它之后的片刻。


一个常见的新手错误,您可以在此站点上找到许多示例,如下所示:

while (trivialTest()) {
    synchronized (lock) {
         processThatTakesSomeTime();
    }
}

这里的问题是,线程释放后几乎接下来要做的事情就是lock再次锁定它。任何其他想要锁定同一个锁的线程都将有效地“进入睡眠状态”,等待第一个线程完成processThatTakesSomeTime()调用。

当第一个线程解锁锁时,这是一场看哪个线程可以再次锁定它的竞赛,但是第一个线程具有巨大的优势,因为它已经在运行,而其他线程可能已经被操作系统“换出”,总是输掉比赛。


解决“新手错误”的一种方法是使用所谓的“乐观锁定”:

while (trivialTest()) {
    Snapshot snapshot;
    boolean success=false;
    while (! success) {
        synchronized (lock) {
            snapshot = takeSnapshotOfSharedData();
        }
        Result result = doSomeComputationWith(snapshot);
        synchronized (lock) {
            if (itStillMakesSenseToPublishResult(snapshot, result)) {
            publishToSharedData(result);
            success = true;
            }
        }
    }
}

这是“乐观的”,因为我们希望内循环每次都能成功。但有时情况并非如此,因为其他一些线程会以与我们的result. 在这种情况下,我们必须把工作扔掉,然后再试一次。

这似乎违反直觉,但仅将锁保持锁定足够长的时间以拍摄快照或验证和发布结果所获得的性能可能比偶尔不得不放弃工作并重试所损失的性能要大得多。

于 2020-07-17T11:32:23.520 回答