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

新手上路,请多包涵

我对在 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

原文由 divz 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 572
2 个回答

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

视窗

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

在Java 5中, Thread.yield() 调用Windows API调用 Sleep(0) 。这具有 清除当前线程的量程 并将其放入 优先级 队列末尾 的特殊效果。换句话说,所有具有相同优先级(和更高优先级)的可运行线程将有机会在下一个给定 CPU 时间的已让出线程之前运行。当它最终被重新安排时,它将返回一个完整的 full quantum ,但不会从屈服时“结转”任何剩余的 quantum 。此行为与非零睡眠略有不同,在非零睡眠中,睡眠线程通常会丢失 1 个量子值(实际上,10 或 15 毫秒刻度的 1/3)。

在 Java 6 中,这种行为发生了变化。 Hotspot VM 现在使用 Windows SwitchToThread() API 调用实现 Thread.yield() 。此调用使当前线程 放弃其 当前时间片,而不是其整个时间片。这意味着根据其他线程的优先级,让出线程可以 在一个中断周期后被调度回来。 (有关时间片的更多信息,请参阅 线程调度 部分。)

Linux

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

  • 所有 其他线程都拥有一块 CPU 之前,让出的线程不会获得另一块 CPU
  • (至少在内核 2.6.8 之后),调度程序对其最近的 CPU 分配的试探法隐含地考虑了线程已经让出的事实——因此,隐式地,已经让出的线程在被调度时可以得到更多的 CPU未来。

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

什么时候使用 yield()

我会说 几乎从来没有。它的行为没有标准定义,通常有更好的方法来执行您可能希望使用 yield() 执行的任务:

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

原文由 Sathwick 发布,翻译遵循 CC BY-SA 3.0 许可协议

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

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

废话少说,这里有一个具体的例子:波前平行模式。这个问题的一个基本实例是计算一个由 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

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

我们想要并行运行的部分是我们计算最小值的步骤。无需过多赘述,每个线程都以交错方式获取行,并依赖于处理上面行的线程计算的值。因此,每个线程都需要稍微落后于处理前一行的线程,但也必须在合理的时间内跟上。更多细节和实现由我自己在 本文档 中介绍。注意 sleep(0) 的用法,它或多或少相当于 yield 的 C 语言。

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

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

抱歉啰啰嗦嗦,希望我说清楚了。

原文由 Tudor 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题