What are the main uses of yield(), and how does it differ from join() and interrupt()?

I am a little bit confused about the use of Thread.yield() method in Java, specifically in the example code below. I've also read that yield() is 'used to prevent execution of a thread'.

My questions are:

  1. I believe the code below result in the same output both when using yield() and when not using it. Is this correct?

  2. What are, in fact, the main uses of yield()?

  3. In what ways is yield() different from the join() and interrupt() methods?

The code example:

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();
}
}
}

I obtain the same output using the code above both with and without using yield():

Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run
103854 次浏览

首先,实际的描述是

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

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

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

interrupt将中断正在调用它的线程,从而导致 异常

yield允许上下文切换到其他线程,因此此线程不会占用进程的整个 CPU 使用量。

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

窗户

在 Hotspot 实现中,Thread.yield()的工作方式 在 Java5和 Java6之间改变。

In Java 5, Thread.yield() calls the Windows API call Sleep(0). This 具有 清除当前线程的量程和 把它放到 优先级别 的队列结束。在其他 所有具有相同优先级的可运行线程(以及具有更高优先级的线程) 优先级)将有机会在生成的线程成为下一个线程之前运行 当它最终被重新调度时,它会回来 一个完整的 全量程,但不“携带”任何 剩余量子从屈服的时间。这种行为是一个 有点不同于非零睡眠,其中睡眠线程 通常会损失1个量子值(实际上,10或15ms 的1/3)。

在 Java6中,这种行为改变了 Thread.yield() using the Windows SwitchToThread() API call. This call 使当前线程 放弃它的当前时间片,但不使其 这意味着依赖于其他的优先级 线程中,屈服的线程可以在一个中断中 < strong > 调度回来 周期后 。(更多信息请参见 线程调度线程调度部分 关于时间片的信息。)

Linux

在 Linux 下,Hotspot 只调用 sched_yield() 这通电话有点不同,可能比低频更严重 Windows:

  • a yielded thread will not get another slice of CPU 直到 < em > all 其他线程拥有一片 CPU;
  • (至少在内核2.6.8以后) ,线程已经让步的事实被调度程序的启发式方法隐式地考虑进去了 在其最近的 CPU 分配上ーー因此,隐式地,一个具有 产生的可以给予更多的中央处理器时,安排在将来。

(有关优先级的更多细节,请参阅 thread scheduling章节 and scheduling algorithms.)

什么时候使用 yield()

I would say 几乎从来没有. Its behaviour isn't standardly defined 一般来说,有更好的方法来执行你的任务 可能希望以屈服率()执行:

  • 如果您尝试使用 只使用一部分 CPU,则可以通过估计线程的 CPU 数量来以更可控的方式完成此操作 在其最后一块处理中使用,然后 睡觉为一些 补偿时间量: 见 睡觉法;
  • 如果你是 等待进程或资源完成或变得可用,有更有效的方法来实现这一点, 例如,通过使用 加入()等待另一个线程完成,使用 wait/notify机制允许一个线程向另一个线程发出信号 that a task is complete, or ideally by using one of the Java 5 并发构造,如 信号灯阻塞队列

关于 yield()interrupt()join()之间的区别——一般而言,不仅仅是在 Java 中:

  1. 屈服: 字面意思是“屈服”意味着放手,放弃,投降。一个屈服的线程告诉操作系统(或者虚拟机,或者其他什么)它愿意让其他线程代替它进行调度。这表明它没有做什么太关键的事情。不过,这只是一个提示,并不能保证有任何效果。
  2. 加入 : 当多个线程在某个句柄、令牌或实体上“加入”时,所有线程都会等待,直到所有其他相关线程完成执行(完全或直到它们自己对应的加入)。这意味着许多线程都完成了它们的任务。然后,可以安排这些线程中的每一个继续其他工作,并能够假定所有这些任务确实已经完成。(不要与 SQL 连接混淆!)
  3. 中断 : 被一个线程用来“戳”另一个正在睡眠、等待或连接的线程——以便它被安排再次继续运行,也许表明它已被中断。(不要与硬件中断混淆!)

For Java specifically, see

  1. 参加:

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

    什么时候加入进来?

  2. 屈服:

  3. Interrupting:

    中断()是邪恶的吗? (这里是 StackOverflow)

线程。屈服()

当我们调用 Thread.production ()方法时,线程调度程序将当前运行的线程保持为 Runnable 状态,并选择另一个优先级相同或更高的线程。如果没有等优先级更高的线程,那么它将重新调度调用屈服()线程。记住,屈服方法不会使线程进入“等待”或“阻塞”状态。它只能使一个线程从运行状态到可运行状态。

加入()

当连接被线程实例调用时,这个线程将告诉当前正在执行的线程等待连接线程完成。当一个任务应该在当前任务完成之前完成时,使用 Join。

我看到这个问题已经被重新激活与赏金,现在问什么是 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

In the next step, each value is replaced by the minimum between itself and its neighbours' values:

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是为了迫使每个线程依次暂停,但是由于处理相邻行的线程在此期间将非常快速地前进,条件变量将被证明是一个灾难性的选择。

As you can see, yield is quite a fine-grain optimization. Using it in the wrong place e.g. waiting on a condition that changes seldomly, will cause excessive use of the CPU.

抱歉长篇大论,希望我说得够清楚了。

屈服()主要用于搁置多线程应用程序。

所有这些方法的不同之处在于,在执行另一个线程的时候,让线程暂停执行,并在线程完成后返回,join ()会让线程的开头一起执行,直到线程结束,在另一个线程结束后再运行,中断()会暂停一段时间线程的执行。

事实上,屈服()的主要用途是什么?

屈服建议 CPU 可以停止当前线程并开始执行具有更高优先级的线程。换句话说,为当前线程分配一个低优先级值,以便为更关键的线程留出空间。

I believe the code below result in the same output both when using yield() and when not using it. Is this correct?

不,两者会产生不同的结果。如果没有屈服点() ,一旦线程获得控制权,它将一次性执行“ Inside run”循环。但是,使用屈服点() ,一旦线程获得控制权,它将打印一次“ Inside run”,然后将控制权移交给其他线程(如果有的话)。如果没有挂起的线程,将再次恢复此线程。因此,每次执行“ Inside run”时,它都会寻找要执行的其他线程,如果没有可用的线程,则当前线程将继续执行。

屈服()在哪些方面不同于 join ()和中断()方法?

为其他重要线程提供空间; join ()用于等待另一个线程完成其执行; 中断()用于中断当前正在执行的线程以执行其他任务。

Thread.yield()使线程从“运行”状态转到“可运行”状态。 注意: 它不会导致线程进入“等待”状态。

目前的答案已经过时,需要根据最近的变化进行修改。

从6到9,Java 版本之间的 Thread.yield()没有 很实际的差异。

;

基于 OpenJDK 源代码(http://hg.openjdk.java.net/)的结论。

如果不考虑对 USDT 探测的 HotSpot 支持(系统跟踪信息在 动态跟踪指南动态跟踪指南中描述)和 JVM 属性 ConvertYieldToSleep,那么 yield()的源代码几乎是相同的。请参阅下面的说明。

Java9 :

Thread.yield()调用特定于 OS 的方法 os::naked_yield():
在 Linux 上:

void os::naked_yield() {
sched_yield();
}

On Windows:

void os::naked_yield() {
SwitchToThread();
}

Java8及以前版本:

Thread.yield()调用特定于 OS 的方法 os::yield():
在 Linux 上:

void os::yield() {
sched_yield();
}

在 Windows 上:

void os::yield() {  os::NakedYield(); }

如您所见,Linux 上的 Thread.yeald()对于所有 Java 版本都是相同的。
让我们看看来自 JDK 8的 Windows os::NakedYield():

os::YieldResult os::NakedYield() {
// Use either SwitchToThread() or Sleep(0)
// Consider passing back the return value from SwitchToThread().
if (os::Kernel32Dll::SwitchToThreadAvailable()) {
return SwitchToThread() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
} else {
Sleep(0);
}
return os::YIELD_UNKNOWN ;
}

Java9和 Java8在额外检查 Win32API 的 SwitchToThread()方法是否存在方面的区别。Java6也有相同的代码。
JDK 7中的 os::NakedYield()源代码略有不同,但是具有相同的行为:

    os::YieldResult os::NakedYield() {
// Use either SwitchToThread() or Sleep(0)
// Consider passing back the return value from SwitchToThread().
// We use GetProcAddress() as ancient Win9X versions of windows doen't support SwitchToThread.
// In that case we revert to Sleep(0).
static volatile STTSignature stt = (STTSignature) 1 ;


if (stt == ((STTSignature) 1)) {
stt = (STTSignature) ::GetProcAddress (LoadLibrary ("Kernel32.dll"), "SwitchToThread") ;
// It's OK if threads race during initialization as the operation above is idempotent.
}
if (stt != NULL) {
return (*stt)() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
} else {
Sleep (0) ;
}
return os::YIELD_UNKNOWN ;
}

自 WindowsXP 和 WindowsServer2003以来,由于 SwitchToThread()方法可用,附加检查已被取消(参见 Msdn 笔记)。

Thread.yield();释放底部线程。

Thread使用的是操作系统线程,因此 Thread.yield();可能会释放硬件线程。

sleep(millis)的实现不好

public class MySleep {
public static void sleep(long millis) throws InterruptedException {
long start = System.currentTimeMillis();
do {
Thread.yield();
if (Thread.interrupted()) {
throw new InterruptedException();
}
} while (System.currentTimeMillis() - start < millis);
}
}

and join()

public class MyJoin {
public static void join(Thread t) throws InterruptedException {
while (t.getState() != Thread.State.TERMINATED) {
Thread.yield();
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
}
public static void main(String[] args) {
Thread thread = new Thread(()-> {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
});
thread.start();
System.out.println("before");
try {
join(thread);
} catch (Exception e) {
}
System.out.println("after");
}
}

即使只有一个硬件线程,也应该可以工作,除非删除 Thread.yield();