Thread :: yield vs Thread :: onSpinWait

时间:2019-05-09 09:48:16

标签: java concurrency java.util.concurrent

标题基本上说明了所有内容,还有我真的很想知道何时使用它们的少量补充。可能很简单-我已经阅读了它们的文档,但仍然不能说出太多区别。

这里有this之类的答案,基本上都是这样:

  

屈服对于忙碌的等待也很有用...

我不太同意他们的原因,原因很简单: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)这个名字很能说明我应该在忙碌的旋转中使用它。

1 个答案:

答案 0 :(得分:6)

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

那么如何避免纯旋转以及与调度和调度阻塞线程相关的开销?

Thread.yield()是对线程调度程序的提示,如果准备好另一个具有相同或更高优先级的线程,则放弃它的时间片。这样可以避免纯粹的自旋,但不能避免重新安排线程的开销。

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

这些说明有什么用?在纯自旋循环中,处理器将一次又一次地推测性地执行循环,以填满管道。当线程旋转的变量最终改变时,由于违反内存顺序,所有的推测性工作将被丢弃。真浪费!

给处理器的提示可能会阻止流水线推测性地执行自旋循环,直到提交了先前的存储器指令为止。在SMT(超线程)的情况下,这很有用,因为可以为其他硬件线程释放管道。