【问题标题】:Thread Starvation vs Contention?线程饥饿与争用?
【发布时间】:2020-07-17 10:30:58
【问题描述】:

根据Oracle docs关于“线程饥饿”的说法,

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

关于Oracle docs关于线程“线程争用”,

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

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

注意:通过参考What is thread contention?的一些答案,我觉得这些答案不也适用于“线程饥饿”吗?

【问题讨论】:

    标签: java multithreading concurrency contention starvation


    【解决方案1】:

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


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

    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 不兼容的方式更改共享数据。在这种情况下,我们必须把工作扔掉,然后再试一次。

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

    【讨论】:

      猜你喜欢
      • 2012-07-26
      • 1970-01-01
      • 2016-06-23
      • 1970-01-01
      • 2017-12-15
      • 2021-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多