Thread.join()原理

简介

    join()是Thread类的一个方法。t.join()方法阻塞调用此方法的线程(calling thread),直到线程t完成,此线程再继续执行;通常用于在main()主线程内,等待其它线程完成再结束main()主线程。

Thread.join线程状态图.png

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args){

//启动一个子线程
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("threadA run");
}
});
threadA.start();
try {
threadA.join(); //调用join()
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("MainThread run finished.");
}

    在main主线程调用threadA.join()后,threadA线程正常运行,main主线程会等待threadA线程结束后再继续运行。

原理分析

下面看下join()的源码

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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) { //无限期等待直到线程A结束
while (isAlive()) { //threadA线程状态
wait(0); //threadA.wait(0),让执行这个方法的线程(主线程)阻塞
}
} else { // 等待固定时间,如果线程A还没结束,那么就不等待了。
while (isAlive()) { //threadA线程状态
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay); //threadA.wait(delay),让执行这个方法的线程(主线程)阻塞
now = System.currentTimeMillis() - base;
}
}
}

public final synchronized void join(long millis, int nanos) throws InterruptedException {

if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}

if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}

if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}

join(millis);
}

    这里使用synchronized是因为在join()里面调用了wait(),此时要进入join(long millis)方法必须持有threadA线程对象锁,也就是主线程持有了threadA这个对象的锁。
下面重点看这一块的代码

1
2
3
while (isAlive()) {   //threadA线程状态
wait(0); //threadA.wait(0),让执行这个方法的线程(主线程)阻塞
}

    如果threadA线程是活跃的,则循环调用threadA.wait(0),此时正在执行的线程(main主线程)释放threadA线程的对象锁,其他线程可以竞争锁并进入threadA.join(0)。一旦threadA线程执行完毕(状态为TERMINATED),JVM会调用lock.notify_all(thread),唤醒持有threadA这个对象锁的线程,至此阻塞在threadA对象上的线程,即主线程可以继续执行后面的内容。

总结

    join()方法的底层是通过wait() 实现的。当main主线程调用threadA.join()时,main线程会获得threadA线程对象锁,只有这样才能执行synchronized join()方法内,调用threadA线程对象的wait()。目的是让持有这个threadA线程对象锁的线程都进入等待,等待threadA线程执行完毕。然后JVM底层lock.notify_all(thread),唤醒持有threadA对象锁的所有线程。

参考资料