线程 join

Author Avatar
xuanzh.cc 11月 08, 2017
  • 在其它设备中阅读本文章

有时候一个线程A的输入要依赖另外一个线程B的输出,即线程A要等待线程B执行完成后在继续执行,此时就可以用到join方法。

方法签名:

1
2
3
4
5
//调用线程无限等待,知道目标线程执行完毕
public final void join() throws InterruptedException
//调用线程等待指定的时间后,无论目标线程是否执行完毕,都会继续执行
public final synchronized void join(long millis)

实例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(){
@Override
public void run() {
for(int i = 1; i <= 10; i++) {
System.out.println("第" + i + "次输出...");
}
}
};
t.start();
//这里加了 join,主线程会等 t 线程运行完毕
t.join();
System.out.println("main 线程输出...");
}
}

运行结果如下:

可以看到主线程实在 t 线程运行完毕后才继续运行的,如果把 t.join() 这一行注释去掉,则结果如下:

可以看到主线程首先就输出了,没有等待 t 线程执行完毕。

join方法本质:

通过查看jdk源码,可以看到 join() 方法实际调用的是 join(0)

1
2
3
public final void join() throws InterruptedException {
join(0);
}

再看看join(long millis)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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");
}
//入参为0
if (millis == 0) {
while (isAlive()) {
wait(0); //这里的 0 代表无限等待,直到被notify
}
}
//等待一定的时间
else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}

实际上 join 方法调用的是 thread 实例的wait方法,由于该方法加了 synchronized 关键字,所以在调用 wait 方法之前,已经获取了目标对象(thread)这个监视器(锁),join 方法可以看成与如下的代码等效:

1
2
3
4
5
synchronized(thread){
...
wait(0)
...
}

就是让调用线程在目标线程上等待,直到目标线程执行完毕,然后目标线程通过调用notifyAll 方法,唤醒目标线程的等待队列里面的线程。当然这些notifyAll 是系统帮我们调用的,所以最好不要在 thread 对象上调用 wait 或者 notify, notifyAll 方法,以免影响系统的工作。