Java 线程::屈服vs线程::onSpinWait

Java 线程::屈服vs线程::onSpinWait,java,concurrency,java.util.concurrent,Java,Concurrency,Java.util.concurrent,这个标题基本上说明了一切,还有一点我真的很想知道什么时候使用它们。这可能很简单——我已经阅读了它们的文档,但仍然看不出有多大区别 这里的答案基本上是这样的: 屈服对于忙碌的等待也是有用的 我不太同意他们的观点,原因很简单,ForkJoinPool在内部使用Thread::yield,这是jdk领域最近的一个新增加 真正困扰我的是在jdk中也有这样的用法(StampledLock::trydereaderOverflow): 因此,似乎在某些情况下,一方比另一方更受欢迎。不,我没有一个实际的例子,

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

这里的答案基本上是这样的:

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

我不太同意他们的观点,原因很简单,
ForkJoinPool
在内部使用
Thread::yield
,这是jdk领域最近的一个新增加

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


因此,似乎在某些情况下,一方比另一方更受欢迎。不,我没有一个实际的例子,我可能需要这个-我实际使用的唯一一个是
Thread::onSpinWait
,因为1)我碰巧在busy wait中2)这个名字很自然,我应该在busy spin中使用它。

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

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

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

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

这些说明有什么用?在纯自旋循环中,处理器会反复推测地执行循环,填满管道。当线程正在旋转的变量最终更改时,由于内存顺序冲突,所有推测工作都将被抛出。真是浪费


对处理器的提示可以防止管道在提交之前推测性地执行自旋循环。在SMT(超线程)的上下文中,这是很有用的,因为管道将被释放出来用于其他硬件线程。

您能描述一下需要旋转等待的场景吗?作为一个Java用户而不是实现者,我从来都不需要这样做。当然,在内核编程中,自旋锁无处不在。但是在Java中呢?我想不出一个用例,在这个用例中我会旋转等待而不是使用锁、监视器或异步回调。(我不是在挑剔。如果答案是几乎没有人应该使用它们中的任何一个,那么问哪一个可能是错误的问题。)@JohnKugelman的可能副本我从来都不需要-我只知道该方法存在,它的文档中有一个与我拥有的非常接近的示例,因此决定使用它,希望它能以某种方式提高性能。我一开始也不需要忙着旋转——这感觉就像是我应该绝对尝试的事情。@Torben考虑到我在那个问题上给出了一个答案,我不认为这是重复的。API示例显示了一个在volatile标志上旋转的循环。如果您正在实现一个低级锁定原语,那么这很好。大多数阅读它的人可能不是这样。他们应该远离这种习语。在变量上旋转只适用于完全的专家和完全的初学者。因此,如果我们期望另一个线程已经在满足条件的不同CPU(核心)上运行,
onSpinWait()
是正确的选择,而
yield()
是正确的选择,如果我们期望另一个线程没有CPU时间。不幸的是,我们不知道,所以问题中显示的示例代码使用一些基于随机的启发式方法来决定何时调用哪个方法。
    else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
        Thread.yield();
    else
        Thread.onSpinWait();
    return 0L;