1、Thread::join 方法

在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如主线程需要使用子线程处理的数据时,就要用到 join 方法了。

线程 A 执行了线程 B 的 join 方法时,则表示当前线程 A 等待线程 B 终止之后,才从 join 方法返回。Thread 提供了 join(),join(long millis) 和 join(long millis, int nanos) 三个方法。方法 join 的作用是等待线程对象销毁。示例代码如下:

  1. package com.yj.thread;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * @description: 线程的 join 方法
  5. * @author: erlang
  6. * @since: 2021-02-08 21:39
  7. */
  8. public class JoinThread {
  9. public static void main(String[] args) throws InterruptedException {
  10. Thread previous = Thread.currentThread();
  11. for (int i = 0; i < 10; i++) {
  12. // 每个线程拥有前一个线程的引用,需要等待前一个线程终止,才能从等待中返回
  13. Thread thread = new Thread(new Runner(previous), "Thread_" + i);
  14. thread.start();
  15. previous = thread;
  16. }
  17. TimeUnit.SECONDS.sleep(5);
  18. }
  19. private static class Runner implements Runnable {
  20. private final Thread thread;
  21. public Runner(Thread thread) {
  22. this.thread = thread;
  23. }
  24. @Override
  25. public void run() {
  26. try {
  27. thread.join();
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. System.out.println(Thread.currentThread().getName() + " state is " + Thread.currentThread().getState());
  32. System.out.println(thread.getName() + " state is " + thread.getState() + "\r\n");
  33. }
  34. }
  35. }

输出结果如下:

  1. Thread_0 state is RUNNABLE
  2. main state is TERMINATED
  3. Thread_1 state is RUNNABLE
  4. Thread_0 state is TERMINATED
  5. Thread_2 state is RUNNABLE
  6. Thread_1 state is TERMINATED
  7. Thread_3 state is RUNNABLE
  8. Thread_2 state is TERMINATED
  9. Thread_4 state is RUNNABLE
  10. Thread_3 state is TERMINATED
  11. Thread_5 state is RUNNABLE
  12. Thread_4 state is TERMINATED
  13. Thread_6 state is RUNNABLE
  14. Thread_5 state is TERMINATED
  15. Thread_7 state is RUNNABLE
  16. Thread_6 state is TERMINATED
  17. Thread_8 state is RUNNABLE
  18. Thread_7 state is TERMINATED
  19. Thread_9 state is RUNNABLE
  20. Thread_8 state is TERMINATED

从上述输出可以看到,每个线程终止的前提是前驱线程的终止,每个线程等待前驱线程终止后,才从 join 方法返回。这里涉及了等待/通知机制,即等待前驱线程结束,接收线程结束通知。

join 方法的源码如下,可以看到 join 方法的逻辑结构和前面提到的等待/通知经典范式一致,即加锁、循环和处理逻辑三个步骤。

  1. public final synchronized void join(long millis) throws InterruptedException {
  2. long base = System.currentTimeMillis();
  3. long now = 0;
  4. if (millis < 0) {
  5. throw new IllegalArgumentException("timeout value is negative");
  6. }
  7. if (millis == 0) {
  8. while (isAlive()) {
  9. wait(0);
  10. }
  11. } else {
  12. while (isAlive()) {
  13. long delay = millis - now;
  14. if (delay <= 0) {
  15. break;
  16. }
  17. wait(delay);
  18. now = System.currentTimeMillis() - base;
  19. }
  20. }
  21. }