thread.join 的含义是当前线程需要等待 previousThread 线程终止之后才从previousThread.join 返回。简单来说,就是上一个线程还没有执行完之前,当前线程会一直阻塞在 join 方法处。
join 为什么阻塞的是主线程呢?
先来看看 Thread.join 方法做了什么事情。
public class Thread implements Runnable {
...
public final void join() throws InterruptedException {
join(0);
}
...
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) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
...
}
从 join 方法的源码来看,join 方法的本质调用的是 Object 中的 wait 方法实现线程的阻塞,wait 方法的实现原理我们在后续的文章再说详细阐述。但是我们需要知道的是,调用 wait 方法必须要获取锁,所以 join 方法是被 synchronized 修饰的,synchronized 修饰在方法层面相当于 synchronized(this),this 就是previousThread 本身的实例。
有很多人不理解 join 为什么阻塞的是主线程呢?
不理解的原因是阻塞主线程的方法是放在 previousThread 这个实例作用,让大家误以为应该阻塞 previousThread 线程。实际上主线程会持有 previousThread 这个对象的锁,然后调用 wait 方法去阻塞,而这个方法的调用者是在 主线程 中的。所以造成主线程阻塞。
为什么 previousThread 线程执行完毕就能够唤醒主线程呢?是在什么时候唤醒的?
通过 wait方 法阻塞的线程,需要通过 notify 或者 notifyall 来唤醒。所以在线程执行完毕以后会有一个唤醒的操作,只是我们不需要关心。
Thread.join 其实底层是通过 wait/notifyall 来实现线程的通信达到线程阻塞的目的;当线程执行结束以后,会触发两个事情,第一个是设置 native 线程对象为null、第二个是通过 notifyall 方法,让等待在 previousThread 对象锁上的 wait 方法被唤醒。
什么时候会使用 Thread.join?
在实际应用开发中,我们很少会使用 thread.join。在实际使用过程中,我们可以通过 join 方法来等待线程执行的结果,其实有点类似 future/callable 的功能。
我们通过以下伪代码来说明 join 的使用场景:
public void joinDemo(){
....
Thread t = new Thread(payService);
t.start();
....
insertData();
//后续的处理,需要依赖 t 线程的执行结果,可以在这里调用 join 方法等待 t 线程执行结束
t.join();
}