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

IT小君   2021-10-09T08:12:18

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

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

Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run
评论(9)
IT小君

来源: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()等待另一个线程完成,使用等待/通知机制允许一个线程向另一个发出任务已完成的信号,或者最好使用 Java 5 并发构造之一,例如Semaphore阻塞队列
2021-10-09T08:12:18   回复
IT小君

我看到这个问题已被重新激活,现在询问实际用途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)或多或少相当于 C 的yield.

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

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

抱歉我说了这么长的废话,希望我说清楚了。

2021-10-09T08:12:18   回复
IT小君

关于yield(),interrupt()join()-之间的区别一般来说,不仅仅是在 Java 中:

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

对于 Java,请参阅

  1. 加盟:

    如何使用Thread.join?(在 StackOverflow 上)

    什么时候加入线程?

  2. 产量:

  3. 中断:

    Thread.interrupt() 是邪恶的吗?(在 StackOverflow 上)

2021-10-09T08:12:19   回复
IT小君

首先,实际描述是

使当前正在执行的线程对象暂时暂停并允许其他线程执行。

现在,很可能你的主线程会run在新线程的方法被执行之前执行循环五次,所以所有的调用yield都会在主线程中的循环执行之后发生。

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

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

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

2021-10-09T08:12:19   回复
IT小君

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

从 6 到 9,Java 版本之间没有实际区别Thread.yield()

TL; 博士;

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

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

爪哇 9

Thread.yield()调用特定操作系统的方法os::naked_yield()
在 Linux 上:

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

在 Windows 上:

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

Java 8 及更早版本:

Thread.yield()调用特定操作系统的方法os::yield()
在 Linux 上:

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

在 Windows 上:

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

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

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

Java 9 和 Java 8 的区别在于额外检查 Win32 API 的SwitchToThread()方法是否存在Java 6 中存在相同的代码。 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 ;
}

由于SwitchToThread()方法自 Windows XP 和 Windows Server 2003 起可用,因此附加检查已被删除(请参阅msdn 注释)。

2021-10-09T08:12:19   回复
IT小君

实际上,yield() 的主要用途是什么?

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

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

NO,两者会产生不同的结果。如果没有 yield(),一旦线程获得控制权,它将一次性执行“内部运行”循环。但是,使用 yield(),一旦线程获得控制权,它将打印一次“内部运行”,然后将控制权移交给其他线程(如果有)。如果没有线程处于挂起状态,则该线程将再次恢复。因此,每次执行“内部运行”时,它都会寻找其他线程来执行,如果没有可用线程,则当前线程将继续执行。

yield() 与 join() 和 interrupt() 方法有哪些不同?

yield() 用于为其他重要线程留出空间,join() 用于等待另一个线程完成其执行,interrupt() 用于中断当前正在执行的线程以执行其他操作。

2021-10-09T08:12:19   回复
IT小君

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

2021-10-09T08:12:19   回复
IT小君

线程.yield()

当我们调用 Thread.yield() 方法时,线程调度器将当前运行的线程保持为 Runnable 状态,并选择另一个具有相同优先级或更高优先级的线程。如果没有相等和更高优先级的线程,则它会重新调度调用 yield() 线程。请记住,yield 方法不会使线程进入 Wait 或 Blocked 状态。它只能使一个线程从Running State 变为Runnable State。

加入()

当线程实例调用 join 时,该线程会告诉当前正在执行的线程等待 Joining 线程完成。Join 用于在当前任务完成之前应该完成的任务的情况。

2021-10-09T08:12:19   回复
IT小君

yield() 主要用于暂停多线程应用程序。

所有这些方法的区别是 yield() 在执行另一个线程时暂停线程并在该线程完成后返回,join() 将把线程的开始一起执行直到结束,并且在该线程完成后运行另一个线程结束,interrupt() 将停止线程的执行一段时间。

2021-10-09T08:12:20   回复