关于join官方的解释是 Waits for this thread to die. 也就是等待一个线程结束。可以通过对另一个线程对象调用join()方法可以等待其执行结束,然后才继续往下执行自身线程。
join场景
public class Main {public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"线程执行..."+i);}},"T1");t1.start();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"线程执行..."+i);}}}
...T1线程执行...81T1线程执行...82main线程执行...50T1线程执行...83T1线程执行...84main线程执行...51main线程执行...52T1线程执行...85T1线程执行...86main线程执行...53main线程执行...54T1线程执行...87...
可以看到正常两个线程是交替执行的。如果我们想线程t1执行完再执行main线程呢,这里就需要使用join了:
public class Main {public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"线程执行..."+i);}},"T1");t1.start();// 调用t1的join方法,当前线程等待,等待t1线程执行后再执行当前线程try {t1.join();} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"线程执行..."+i);}}}
执行效果:
....T1线程执行...90T1线程执行...91T1线程执行...92T1线程执行...93T1线程执行...94T1线程执行...95T1线程执行...96T1线程执行...97T1线程执行...98T1线程执行...99main线程执行...0main线程执行...1main线程执行...2main线程执行...3main线程执行...4main线程执行...5main线程执行...6....
此时子线程T1执行完之后才执行main线程。
Join方法
join方法还支持几个参数:

join的使用实例
现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完之后执行,T3在T2执行完后执行?
使用join是最简单的方案,实现的代码如下:
/*** @author wcc* @date 2021/8/21 20:46* 现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?*/public class JoinDemo {public static void main(String[] args) {Thread t1=new Thread(new Runnable() {@Overridepublic void run() {System.out.println("t1 is running...");}});//初始化线程二Thread t2=new Thread(new Runnable() {@Overridepublic void run() {try {// 调用t1的join,意味着再t1执行后,才执行此线程t1.join();} catch (InterruptedException e) {e.printStackTrace();}finally {System.out.println("t2 is running...");}}});//初始化线程三Thread t3=new Thread(new Runnable() {@Overridepublic void run() {try {// 调用t2的join,意味着再t1执行后,才执行此线程t2.join();} catch (InterruptedException e) {e.printStackTrace();}finally {System.out.println("t3 is running...");}}});t1.start();t2.start();t3.start();}}
执行结果:
t1 is running...t2 is running...t3 is running...
在t2线程中t2本身就是调用线程,所谓的调用线程是指调用了t.join()方法的线程,而被调用的对象指的是调用join方法的线程对象,即t1。_所以这三个线程按照_t1 -> t2 -> t3的顺序执行了。
join原理
/*** 等待该线程终止的时间最长为millis毫秒,超时时间为0意味着要一直等下去* @param millis 以毫秒为单位的等待时间* @throws InterruptedException*/public final synchronized void join(long millis)throws InterruptedException {//获取启动的时间戳,用于计算当前时间long base = System.currentTimeMillis();//当前时间long now = 0;if (millis < 0) { //等待时间不能小于0 否则抛出IllegalArgumentException异常终止程序throw new IllegalArgumentException("timeout value is negative");}//判断是否携带阻塞的超时时间,等于0则表示没有设置超时时间//如果超时时间为0 则意味着一直要等待该线程执行完(无限等待)if (millis == 0) {//需要注意的是,如果当前线程未被启动或者已经终止,则isAlive方法返回false//即意味着join方法不会生效while (isAlive()) {//isAlive()方法:判断当前线程是否处于活动状态//活动状态就是线程已经启动并且尚未终止wait(0);}} else { //设置了超时时间//需要注意的是,如果当前线程未被启动或者已经终止,则isAlive方法返回false//即意味着join方法不会生效while (isAlive()) {,//计算剩余时间long delay = millis - now;if (delay <= 0) { //如果剩余等待的时间小于等于0,则终止等待break;}//等待指定时间wait(delay);//获取此次循环执行的时间now = System.currentTimeMillis() - base;}}}
- 如果想要join方法正常生效,调用join方法的线程对象必须已经调用了start()方法并且未进入终止状态。
- join方法的本质调用的是Object中的wait方法实现线程的阻塞。
- Thread.join其实底层是通过wait/notifyall来实现线程的通信达到线程阻塞的目的;当线程执行结束以后,会触发两个事情,第一个是设置native线程对象为null、第二个是通过notifyall方法,让等待在previousThread对象锁上的wait方法被唤醒。
