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

div*_*ivz 98 java multithreading yield-keyword concurrent-programming

yield()对Java 中方法的使用有点困惑,特别是在下面的示例代码中.我还读过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();
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

使用和不使用上面的代码我获得相同的输出yield():

Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run
Run Code Online (Sandbox Code Playgroud)

Sat*_*ick 93

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

视窗

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

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

在Java 6中,此行为已更改.Hotspot VM现在 Thread.yield()使用Windows SwitchToThread()API调用实现.这个调用使当前线程放弃其当前时间片,但不是它的整个量子.这意味着根据其他线程的优先级,可以在稍后的一个中断周期中调度退出线程.(有关时间片的更多信息,请参阅有关线程调度的部分.)

Linux的

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

  • 所有其他线程都有一片CPU 之前,一个屈服的线程不会得到另一片CPU ;
  • (至少在内核2.6.8以后),调度程序对其最近的CPU分配的启发式隐含地考虑了线程已经产生的事实 - 因此,隐含地,已经产生的线程可以在调度时给予更多的CPU.未来.

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

什么时候用yield()

几乎从不说.它的行为没有标准定义,并且通常有更好的方法来执行您可能想要使用yield()执行的任务:

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

  • "剩下的量子","整个量子" - 在某个地方有人忘记了"量子"这个词的含义 (17认同)
  • @kbolino-... *拉丁语:“多达”,“多少” *。我看不出这与上面的用法有何矛盾。这个词仅表示已描述数量的东西,因此对我来说,将其分为使用过的部分和剩余部分似乎完全合理。 (2认同)

Tud*_*dor 37

我看到这个问题已经通过赏金重新激活,现在询问实际用途yield是什么.我将从我的经验中给出一个例子.

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

但足够的bab呀学语,这是一个具体的例子:波前平行模式.该问题的基本实例是在填充0和1的二维数组中计算1的单个"岛"."孤岛"是一组垂直或水平相邻的单元:

1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1
Run Code Online (Sandbox Code Playgroud)

这里我们有两个1s岛:左上角和右下角.

一个简单的解决方案是对整个数组进行第一次传递,并用递增计数器替换1个值,以便最后每个1用行主序中的序列号替换:

1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8
Run Code Online (Sandbox Code Playgroud)

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

1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4
Run Code Online (Sandbox Code Playgroud)

我们现在可以轻松确定我们有两个岛屿.

我们想要并行运行的部分是我们计算最小值的步骤.在不进行太多细节的情况下,每个线程以交错的方式获取行,并依赖于处理上面行的线程计算的值.因此,每个线程需要稍微滞后于处理前一行的线程,但也必须在合理的时间内保持.我自己在本文档中介绍了更多细节和实现.注意其用法sleep(0)或多或少是C的等价物yield.

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

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

抱歉长期唠叨,希望我清楚自己.

  • @PetrPudlák:是的.我使用线程信号进行基准测试,在这种情况下性能差异很大.由于条件可以很快变为真(这是关键问题),因此条件变量太慢,因为操作系统将线程置于保持状态,而不是使用`yield`放弃CPU很短的持续时间. (3认同)

ein*_*ica 12

关于之间的差异yield(),interrupt()以及join()-在一般情况下,不只是在Java中:

  1. 屈服:从字面上看,"屈服"意味着放手,放弃,投降.一个让步的线程告诉操作系统(或虚拟机,或者不是什么)它愿意让其他线程被安排.这表明它没有做太重要的事情.但这只是一个提示,并不能保证会产生任何影响.
  2. 加盟:当多个线程对某些手柄,或令牌,或实体"加入",所有的人等待,直到所有其他相关的线程都执行完毕(完全或高达自己对应的连接).这意味着一堆线程都完成了他们的任务.然后可以安排这些线程中的每一个继续其他工作,从而能够假设所有这些任务确实完成.(不要与SQL连接混淆!)
  3. 中断:由一个线程用来"戳"另一个正在休眠或等待或加入的线程 - 这样它就会被安排再次继续运行,可能会显示它已被中断.(不要与硬件中断混淆!)

具体来说,请参阅

  1. 加盟:

    如何使用Thread.join?(这里是StackOverflow)

    什么时候加入线程?

  2. 产量:

  3. 中断:

    Thread.interrupt()是邪恶的吗?(这里是StackOverflow)


MBy*_*ByD 9

首先,实际描述是

导致当前正在执行的线程对象暂时暂停并允许其他线程执行.

现在,您的主线程很可能在执行run新线程的方法之前执行循环五次,因此所有调用yield仅在主线程中的循环执行后才会发生.

join将停止当前线程,直到被调用的线程join()完成执行.

interrupt将中断正在调用它的线程,导致InterruptedException.

yield 允许上下文切换到其他线程,因此该线程不会消耗进程的整个CPU使用率.