java - Thread::yield 与 Thread::onSpinWait

标签 java concurrency java.util.concurrent

好吧,标题基本上说明了一切,还有一点我真的很想知道什么时候使用它们。它可能很简单——我已经阅读了它们的文档,但仍然看不出它们的区别。

有像this这样的答案这里基本上说:

Yielding also was useful for busy waiting...

我不太同意他们的观点,原因很简单,ForkJoinPool 在内部使用了 Thread::yield,这是 jdk 世界中最近才添加的。

真正困扰我的是在 jdk 中也有这样的用法(StampledLock::tryDecReaderOverflow):

    else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
        Thread.yield();
    else
        Thread.onSpinWait();
    return 0L;

所以似乎有些情况下一个人会比另一个人更受欢迎。不,我没有一个我可能需要这个的实际例子 - 我实际使用的唯一一个是 Thread::onSpinWait 因为 1) 我碰巧忙着等待 2) 这个名字非常多不言自明,我应该在繁忙的旋转中使用它。

最佳答案

阻塞线程时,有几种策略可供选择:自旋、wait()/notify(),或两者的组合。对变量进行纯自旋是一种非常低延迟的策略,但它可能会使其他争用 CPU 时间的线程饿死。另一方面,wait()/notify() 将为其他线程释放 CPU,但在取消调度/调度线程时可能会花费数千个 CPU 周期的延迟。

那么我们如何才能避免纯自旋以及与取消调度和调度阻塞线程相关的开销呢?

Thread.yield() 提示线程调度程序在另一个具有相同或更高优先级的线程准备就绪时放弃其时间片。这避免了纯旋转,但没有避免重新安排线程的开销。

最新添加的是 Thread.onSpinWait(),它插入特定于体系结构的指令以提示处理器线程处于自旋循环中。在 x86 上,这可能是 PAUSE 指令,在 aarch64 上,这是 YIELD 指令。

这些指令有什么用?在纯自旋循环中,处理器将推测性地一遍又一遍地执行循环,填满流水线。当线程正在旋转的变量最终发生变化时,所有的推测工作都将由于内存顺序冲突而被抛出。多么浪费!

对处理器的提示可以防止流水线推测性地执行自旋循环,直到提交先前的内存指令。在 SMT(超线程)的上下文中,这很有用,因为将为其他硬件线程释放管道。

关于java - Thread::yield 与 Thread::onSpinWait,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56056711/

相关文章:

c# - WCF 如何处理实例化和并发性?

java - 如何延迟方法返回直到文件完全读取

database - 为什么我们需要意向锁?

java - 为什么 ConcurrentSkipListSet 升序迭代器 'faster' 而不是降序迭代器?

java - 多线程与 ThreadPoolExecutor

java - 制作一个 JFrame 按钮创建一个警告对话框。 Java-Netbeans

java - 为什么有时我们将行为与 Java 中的类分开

java - java.util.Date 是否使用 TimeZone?

java - ArrayBlockingQueue : concurrent put and take

java - "main"org.apache.spark.SparkException : Task not serializable