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;
}
@Override
public 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 RUNNABLE
main state is TERMINATED
Thread_1 state is RUNNABLE
Thread_0 state is TERMINATED
Thread_2 state is RUNNABLE
Thread_1 state is TERMINATED
Thread_3 state is RUNNABLE
Thread_2 state is TERMINATED
Thread_4 state is RUNNABLE
Thread_3 state is TERMINATED
Thread_5 state is RUNNABLE
Thread_4 state is TERMINATED
Thread_6 state is RUNNABLE
Thread_5 state is TERMINATED
Thread_7 state is RUNNABLE
Thread_6 state is TERMINATED
Thread_8 state is RUNNABLE
Thread_7 state is TERMINATED
Thread_9 state is RUNNABLE
Thread_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;
}
}
}