Thread :: yield vs Thread :: onSpinWait

Eug*_*ene 8 java concurrency java.util.concurrent

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

有喜欢回答这个位置,基本上说:

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

我不太同意他们的说法,原因很简单,即内部ForkJoinPool使用Thread::yield,这是jdk世界中的新增功能。

真正困扰我的是jdk中的用法(StampledLock::tryDecReaderOverflow):

    else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
        Thread.yield();
    else
        Thread.onSpinWait();
    return 0L;
Run Code Online (Sandbox Code Playgroud)

因此,似乎在某些情况下,一个会比另一个更受欢迎。而且,没有,我没有实际的示例可能需要使用它-我实际使用的唯一示例是Thread::onSpinWait因为1)我碰巧忙于等待2)这个名字很能说明我应该在忙碌中使用它旋转。

Eri*_*ric 6

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

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

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

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

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

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

  • 因此,如果我们期望另一个线程已经在满足条件的另一个CPU(核心)上运行,那么“ onSpinWait()”是正确的事情,而如果我们期望另一个线程没有CPU时间,那么“ yield()”就是正确的事情。不幸的是,我们不知道,因此问题中显示的示例代码使用了一些基于随机的启发式方法来决定何时调用哪种方法。 (3认同)