如果我们有足够的处理器来服务所有线程,那么Thread.yield()会做任何事情吗?

39

如果我们在一台有两个处理器的机器上有两个运行中的线程,并且在其中一个线程中调用了Thread.yield(),那么是否合理认为什么都不会发生(调度程序将基本忽略请求),因为我们有足够的处理器来为运行中的线程提供服务?


12
值得一提的是,调用Thread.yield()是一种轻微的代码异味。它不像调用sleep()那样糟糕,但当我看到它时我肯定会感到怀疑。通常情况下,这是由正在轮询变化的线程使用的,而他们应该等待监视器、监听事件、注册异步回调等。 - John Kugelman
@JohnKugelman,看起来如果涉及等待,Thread::onSpinWait在Java-9中被添加了。不过我仍然无法确定何时使用哪个。 - Eugene
3个回答

27
每当一个线程调用Thread.yield()方法时,它向线程调度程序提示它准备暂停执行。线程调度程序可以忽略此提示。
如果任何线程执行yield方法,则线程调度程序将检查是否有任何可运行(等待执行)的线程与此线程具有相同或更高的优先级。如果处理器发现任何具有更高或相同优先级的线程,则将切换到新线程。如果没有,则当前线程继续执行。
由于在您的示例中,您有足够的处理器来服务所有线程(它们正在运行,而不是等待在可运行状态下); Thread.yield()将无效,您的线程将继续执行。
关于Windows的说明,来自Microsoft DOTNet:
此方法等效于使用平台调用调用本机Win32 SwitchToThread函数。
让出对正在执行调用线程的处理器进行限制。即使该处理器处于空闲状态或正在运行低优先级的线程,操作系统也不会将执行切换到另一个处理器。如果在当前处理器上没有其他准备好执行的线程,则操作系统不会放弃执行
因此,在某些情况下可能存在注意事项。

1
在你的回答中,你没有触及可用于运行线程的处理器数量。如何考虑这个因素?具体而言,我的例子涉及两个处理器和两个线程。 - Dave
4
请注意,您的操作系统肯定会有许多其他正在运行的进程,并且其中一些可能需要CPU时间。 - John Kugelman
4
@Dave, Re,“...涉及处理器数量...”处理器的数量并不重要。如果Thread.yield()有任何作用,它会告诉调度器:调用者(a)仍有任务需要完成,(b)不想放弃正在运行的处理器,但是(c)如果其他线程需要处理器时,愿意放弃处理器。唯一重要的处理器是调用者正在运行的处理器。唯一重要的数字是希望运行在处理器上的其他线程的数量(具体来说,这个数字是零还是大于零)。 - Solomon Slow
永远不会有任何具有更高优先级的准备好的线程 - 它们已经在运行了 :) - Martin James

12

Thread.yield() 已经过时。除非你的程序将在实现了 协作式多任务处理 的平台上运行,或在仍使用 绿色线程 的 JVM 上运行,否则调用它是没有意义的。

Thread.yield() 的标准库文档有效地说明了 yield() 可以不做任何事情。


4
我怀疑它已经过时了——在JDK类的内部查看——它有很多用法。 - Eugene
1
写库时,你不知道它将在哪里运行。 - erickson

8

我一直认为Thread::yield应该被Thread::onSpinWait(自Java 9起)替换 - 这只是一种“较弱”的让渡,直到我看到在StampedLock中使用它们 同时

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

所以我认为它并非过时。在 jdk 源代码内部,它有很多用途,甚至相对较新的 ForkJoinPool 都使用了 Thread::yield

实际上,我仅在繁忙自旋中使用 Thread::onSpinWait - 因为从其名称至少可以清楚地知道何时使用它;而关于 yield,则不太清楚何时如何使用,所以我无法确定。

仅代表我的个人看法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接