> 文章列表 > Thread.join()会使什么线程处于等待状态?

Thread.join()会使什么线程处于等待状态?

Thread.join()会使什么线程处于等待状态?

今天天气格外好,天空湛蓝,白云飘逸,阳光明媚,张三准备出门游玩,掏出手机打了个滴滴,一看需要等待3分钟,那就等等好了,谁叫司机离他有点远呢?3分钟后,司机达到,于是乎,上车出发,完美。

Thread.join()会使什么线程处于等待状态?

在这个例子中,将张三类比为主线程,打车后,主线程的操作已经完成,滴滴师傅需要3分钟才能到达,这个类比为子线程,主线程需要等到子线程执行完成后,才能继续往下执行。

那么,怎样才能满足这样的需求呢?

1.为什么需要join()方法

为了解决这样的需求,Thread类中为我们提供了join()方法,可以实现这种效果。

以下是通过来实现上述场景。

1)未调用join()方法。

import lombok.SneakyThrows;import java.util.concurrent.TimeUnit;public class ThreadJoinTest {@SneakyThrowspublic static void main(String[] args) {CustomRunnable customRunnable = new CustomRunnable();customRunnable.setCustomer("张三");Thread thread = new Thread(customRunnable);thread.start();// thread.join();System.out.println("滴滴师傅已到达,现在出发!");}static class CustomRunnable implements Runnable {private String customer;@SneakyThrows@Overridepublic void run() {System.out.println(customer + "刚刚下单了,距离1km,预计3分钟到达!");TimeUnit.MINUTES.sleep(3);}public void setCustomer(String customer) {this.customer = customer;}}
}

执行结果:

Thread.join()会使什么线程处于等待状态?

当我们为调用join()方法时,主线程因为执行快,提前执行完成,而子线程需要3分钟才能执行完成,因此未能达到我们想要的效果。

2)调用join()方法

import lombok.SneakyThrows;import java.util.concurrent.TimeUnit;public class ThreadJoinTest {@SneakyThrowspublic static void main(String[] args) {CustomRunnable customRunnable = new CustomRunnable();customRunnable.setCustomer("张三");Thread threadA = new Thread(customRunnable);threadA.start();threadA.join();System.out.println("滴滴师傅已到达,现在出发!");}static class CustomRunnable implements Runnable {private String customer;@SneakyThrows@Overridepublic void run() {System.out.println(customer + "刚刚下单了,距离1km,预计3分钟到达!");TimeUnit.MINUTES.sleep(3);}public void setCustomer(String customer) {this.customer = customer;}}
}

这个时候,由于子线程需要执行3分钟。

Thread.join()会使什么线程处于等待状态?

我们结合jpsjstack命令来看看main线程的状态,main线程处于State.WATING状态。

Thread.join()会使什么线程处于等待状态?

执行结果:

Thread.join()会使什么线程处于等待状态?

借助join()方法,已经达到我们想要的效果。

2.join()方法底层实现

我们先来看看底层实现代码:

// 其注释为等待此线程死亡
public final void join() throws InterruptedException {join(0);
}

继续跟踪,可以发现这里有几个步骤:

1)执行isAlive()方法,判断子线程存活状态。

由于我们的millis = 0,所以先执行isAlive()方法,意思是判断调用者所指代的线程是否处于活动状态,因为我们使用的是threadA对象调用join()方法,因此这里的此线程指代threadA所代表的线程,也就是子线程。

2)子线程存活,则暂停主线程

子线程存活,则执行wait(0)方法,wait()方法的目的是使当前线程处于State.WATING状态。因为我们是在主线程中调用的join()方法,所以当前线程一定是主线程。

这里容易产生疑惑的点就是,我们使用的是threadA对象调用的join()方法呀,为什么当前线程却指代的是主线程,其实threadA只能算作调用者。

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;}}
}

isAlive()方法,判断调用者所指代的线程是否处于存活状态。

public final native boolean isAlive();

wait()方法,使当前线程处于State.WATING状态,调用者处于哪个线程,该线程就是当前线程。

public final native void wait(long timeoutMillis) throws InterruptedException;

从上述代码,我们可以得知,join()方法的底层使用wait()方法来实现。使用wait()方法的前提是,其处于同步方法或同步代码块环境中,果不其然join()方法使用了synchronized关键字修饰,我们知道synchronized修饰非静态方法,锁对象是当前对象this,也就是调用者对象threadA。我们来验证一下。

public class ThreadJoinTest {@SneakyThrowspublic static void main(String[] args) {CustomRunnable customRunnable = new CustomRunnable();customRunnable.setCustomer("张三");Thread threadA = new Thread(customRunnable,"threadA");threadA.start();TimeUnit.SECONDS.sleep(1);threadA.join();System.out.println("滴滴师傅已到达,现在出发!");}static class CustomRunnable implements Runnable {private String customer;@SneakyThrows@Overridepublic void run() {System.out.println(customer + "刚刚下单了,距离1km,预计3分钟到达!");// 使用当前线程对象作为锁对象synchronized (Thread.currentThread()) {System.out.println(Thread.currentThread().getName());TimeUnit.MINUTES.sleep(3);}}public void setCustomer(String customer) {this.customer = customer;}}
}

我们在CustomRunnablerun()方法中增加同步代码块,使用Thread.currentThread()作为锁对象,然后打印其名称得知为threadA,这时再来查看main线程的状态,发现其为State.BLOCKED,这也就说明join()方法的锁对象也为threadA。从而证明synchronized修饰非静态方法,锁对象是当前对象this,也就是调用者对象threadA

Thread.join()会使什么线程处于等待状态?
Thread.join()会使什么线程处于等待状态?

3.结论

调用Thread.join(),会使调用者所处的线程转换为State.WATING状态。