1、Thread::join 方法
在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如主线程需要使用子线程处理的数据时,就要用到 join 方法了。
线程 A 执行了线程 B 的 join 方法时,则表示当前线程 A 等待线程 B 终止之后,才从 join 方法返回。Thread 提供了 join(),join(long millis) 和 join(long millis, int nanos) 三个方法。方法 join 的作用是等待线程对象销毁。示例代码如下:
package com.yj.thread;import java.util.concurrent.TimeUnit;/*** @description: 线程的 join 方法* @author: erlang* @since: 2021-02-08 21:39*/public class JoinThread {public static void main(String[] args) throws InterruptedException {Thread previous = Thread.currentThread();for (int i = 0; i < 10; i++) {// 每个线程拥有前一个线程的引用,需要等待前一个线程终止,才能从等待中返回Thread thread = new Thread(new Runner(previous), "Thread_" + i);thread.start();previous = thread;}TimeUnit.SECONDS.sleep(5);}private static class Runner implements Runnable {private final Thread thread;public Runner(Thread thread) {this.thread = thread;}@Overridepublic void run() {try {thread.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " state is " + Thread.currentThread().getState());System.out.println(thread.getName() + " state is " + thread.getState() + "\r\n");}}}
输出结果如下:
Thread_0 state is RUNNABLEmain state is TERMINATEDThread_1 state is RUNNABLEThread_0 state is TERMINATEDThread_2 state is RUNNABLEThread_1 state is TERMINATEDThread_3 state is RUNNABLEThread_2 state is TERMINATEDThread_4 state is RUNNABLEThread_3 state is TERMINATEDThread_5 state is RUNNABLEThread_4 state is TERMINATEDThread_6 state is RUNNABLEThread_5 state is TERMINATEDThread_7 state is RUNNABLEThread_6 state is TERMINATEDThread_8 state is RUNNABLEThread_7 state is TERMINATEDThread_9 state is RUNNABLEThread_8 state is TERMINATED
从上述输出可以看到,每个线程终止的前提是前驱线程的终止,每个线程等待前驱线程终止后,才从 join 方法返回。这里涉及了等待/通知机制,即等待前驱线程结束,接收线程结束通知。
join 方法的源码如下,可以看到 join 方法的逻辑结构和前面提到的等待/通知经典范式一致,即加锁、循环和处理逻辑三个步骤。
public final synchronized void join(long millis) throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {while (isAlive()) {wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}
