yield() 的主要用途是什么,它与 join() 和 interrupt() 有何不同?

2022-08-31 09:33:57

我对在Java中使用Thread.yield()方法有点困惑,特别是在下面的示例代码中。我还读到 yield() 是 “用来防止线程的执行”。

我的问题是:

  1. 我相信下面的代码在使用和不使用它时都会产生相同的输出。这是正确的吗?yield()

  2. 事实上,它的主要用途是什么?yield()

  3. 在哪些方面与 和 方法不同?yield()join()interrupt()

代码示例:

public class MyRunnable implements Runnable {

   public static void main(String[] args) {
      Thread t = new Thread(new MyRunnable());
      t.start();

      for(int i=0; i<5; i++) {
          System.out.println("Inside main");
      }
   }

   public void run() {
      for(int i=0; i<5; i++) {
          System.out.println("Inside run");
          Thread.yield();
      }
   }
}

我使用上面的代码获得相同的输出,无论是否使用:yield()

Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run

答案 1

资料来源:http://www.javamex.com/tutorials/threads/yield.shtml

窗户

在 Hotspot 实现中,Java 5 和 Java 6 之间的工作方式发生了变化。Thread.yield()

在 Java 5 中,调用 Windows API 调用 。这具有清除当前线程的量程并将其置于队列末尾以达到其优先级的特殊效果。换句话说,所有具有相同优先级的可运行线程(以及具有更高优先级的线程)将有机会在下次给定 CPU 时间之前运行生成的线程。当它最终被重新安排时,它将以完整的量子回来,但不会从屈服时“延续”任何剩余的量子。这种行为与非零睡眠略有不同,在非零睡眠中,睡眠线程通常失去1个量子值(实际上,10或15ms滴答的1/3)。Thread.yield()Sleep(0)

在Java 6中,这种行为发生了变化。Hotspot VM 现在使用 Windows API 调用实现。此调用使当前线程放弃其当前时间片,但不会放弃其整个量程。这意味着,根据其他线程的优先级,可以在稍后的一个中断时间段内重新调度产生线程。(有关时间片的详细信息,请参阅线程调度部分。Thread.yield()SwitchToThread()

Linux

在 Linux 下,Hotspot 只需调用 .此调用的后果略有不同,并且可能比在Windows下更严重:sched_yield()

  • 在所有其他线程都有一个 CPU 切片之前,生成的线程不会获得另一个 CPU 切片;
  • (至少在内核 2.6.8 及更高版本中),调度程序对其最近的 CPU 分配的启发式方法隐含地考虑了线程已产生的事实 - 因此,隐式地,在将来调度时,可以向已产生线程提供更多 CPU。

(有关优先级和调度算法的更多详细信息,请参阅线程调度部分。

何时使用?yield()

我会说实际上从来没有。它的行为没有标准定义,通常有更好的方法来执行你可能想要用yield()执行的任务:

  • 如果您尝试仅使用一部分CPU,则可以通过估计线程在其最后一个处理块中使用了多少CPU,然后休眠一段时间以进行补偿,以更可控的方式执行此操作:请参阅sleep()方法;
  • 如果您正在等待进程或资源完成或变得可用,则有更有效的方法可以完成此操作,例如使用 join() 等待另一个线程完成,使用等待/通知机制允许一个线程向另一个线程发出任务已完成的信号,或者理想情况下使用 Java 5 并发构造之一,如信号量阻塞队列

答案 2

我看到这个问题已经用赏金重新激活,现在问实际用途是什么。我将根据我的经验举一个例子。yield

如我们所知,强制调用线程放弃运行它的处理器,以便可以安排另一个线程运行。当当前线程现在已完成其工作但希望快速返回到队列的前面并检查某些条件是否已更改时,这很有用。这与条件变量有何不同? 使线程能够更快地返回到运行状态。当等待条件变量时,线程将挂起,需要等待其他线程发出应继续的信号。 基本上说“允许不同的线程运行,但允许我很快恢复工作,因为我期望我的状态会很快发生变化”。这暗示了旋转繁忙,其中条件可能会迅速变化,但挂起线程会导致较大的性能下降。yieldyieldyield

但是足够大的絮絮叨叨,这里有一个具体的例子:波前平行模式。这个问题的一个基本实例是在填充了0和1的双向数组中计算1的单个“孤岛”。“岛”是一组在垂直或水平方向上彼此相邻的单元格:

1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1

在这里,我们有两个1的岛屿:左上角和右下角。

一个简单的解决方案是首先遍历整个数组,并将 1 个值替换为递增计数器,这样到最后,每个 1 都替换为其按行主序排列的序列号:

1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8

在下一步中,每个值都替换为其自身与其相邻值之间的最小值:

1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4

我们现在可以很容易地确定我们有两个岛屿。

我们想要并行运行的部分是计算最小值的步骤。在不涉及太多细节的情况下,每个线程都以交错方式获取行,并依赖于处理上述行的线程计算的值。因此,每个线程都需要稍微滞后于处理前一行的线程,但也必须在合理的时间内跟上。本文档中提供了更多详细信息和实现。请注意,其用法或多或少是 C 等效于 。sleep(0)yield

在这种情况下,用于强制每个线程依次暂停,但由于处理相邻行的线程在此期间会非常快速地前进,因此条件变量将是一个灾难性的选择。yield

如您所见,这是一个非常细粒度的优化。在错误的地方使用它,例如在很少变化的条件下等待,将导致过度使用CPU。yield

很抱歉长长的胡言乱语,希望我把自己说清楚。


推荐