1. 概述

在本篇文章中,我们将深入探讨 Thread 类中的 yield() 方法。

我们会将它与 Java 中其他并发机制进行对比,并最终分析它的实际应用场景。

2. yield() 方法简析

正如官方文档所述,yield() 提供了一种机制,用于告知线程调度器:“当前线程愿意让出处理器的使用权,但希望尽快被重新调度执行。

⚠️ 调度器可以选择性地采纳或忽略这个提示,而且其行为在不同操作系统上可能有所不同。

下面这段代码展示了两个相同优先级的线程在每次调度后调用 yield() 的情况:

public class ThreadYield {
    public static void main(String[] args) {
        Runnable r = () -> {
            int counter = 0;
            while (counter < 2) {
                System.out.println(Thread.currentThread()
                    .getName());
                counter++;
                Thread.yield();
            }
        };
        new Thread(r).start();
        new Thread(r).start();
    }
}

当我们多次运行上述程序时,会得到不同的输出结果。例如:

运行 1:

Thread-0
Thread-1
Thread-1
Thread-0

运行 2:

Thread-0
Thread-0
Thread-1
Thread-1

✅ 可见 yield() 的行为是非确定性的,且依赖于平台。

3. 与其他并发机制的比较

Java 中还有其他一些控制线程执行顺序的机制,包括来自 Object 类的 wait()notify()notifyAll(),以及来自 Thread 类的 join()sleep()

下面我们看看它们与 yield() 之间的区别。

3.1. yield() vs wait()

  • yield() 是在线程上下文中直接调用的;而 wait() 必须在同步块或方法中、显式获取锁之后才能调用。
  • yield() 不同,wait() 可以指定一个最小等待时间,在此之前不会尝试重新调度该线程。
  • 使用 wait() 后,可以通过对相关对象调用 notify()notifyAll() 来随时唤醒线程。

3.2. yield() vs sleep()

  • yield() 只是启发式地尝试暂停当前线程的执行,不保证何时会被重新调度;
  • sleep() 则可以强制调度器暂停当前线程至少参数指定的时间。

3.3. yield() vs join()

  • 当前线程可以在任意其他线程上调用 join(),表示当前线程需要等待该线程执行完毕后再继续执行。
  • join() 还可以接受一个时间参数,表示当前线程最多等待多久后就恢复执行。

4. yield() 的使用场景

根据官方文档建议,通常情况下我们很少需要使用 yield(),因此除非你非常清楚其行为并有明确目的,否则应尽量避免使用。

尽管如此,yield() 在某些特定场景下仍有价值,例如:

  • 设计自定义的并发控制结构
  • 在计算密集型任务中提升系统响应性

⚠️ 但这些用法都必须配合详细的性能测试和基准验证,以确保能达到预期效果。

5. 总结

本文简要介绍了 Thread 类中的 yield() 方法,并通过代码示例展示了它的行为特点和局限性。

同时我们也对比了它与其他 Java 并发机制的区别,并列举了一些可能适用的场景。

一如既往,本文所有示例代码均可在 GitHub 上找到。


原始标题:Brief Introduction to Java Thread.yield()