之前在学习Java多线程的时候,对join方法理解是:在调用B线程的join方法之后,其他线程只有在B线程执行完毕之后才能执行,但是最近看源码并不是这样的,而是调用B.join()的线程会在B线程执行完毕之后才能执行,其他线程会与B线程交替进行。
下面是jdk8中的关于join方法的实现:
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;
}
}
}
下面的理解以在A线程中调用B.join()为例:
注意到这个方法是一个被synchronized加了锁的方法,也就说明这个方法是线程安全的,锁的对象是this,也就是B。其中最核心部分的代码是下面这几行:
if (millis == 0) {
while (isAlive()) {
wait(0);
}
isAlive()方法判断了B线程是否存活,官方文档中的描述alive的状态为:has been started and has not yet died。其中使用while循环而不是if判断的原因是防止被虚假唤醒。
wait()方法是实现join()功能的关键,注意到当进入到这个方法的时候对B加的锁被释放掉,然后等待被唤醒,从而达到A线程等待B线程执行完毕才能执行的目的。
整体的流程如下:
- A线程调用了B.join()
- 执行B.join(),对B加锁,锁的持有对象是A
- 执行到wait()时,A释放掉对B的锁,A因为没有了B的锁,然后陷入等待被唤醒的状态
- B线程执行,当B执行完毕之后,通过notify()或者notify_all()方法唤醒A(如果B线程中没有显式的使用这两个方法,jvm会在B线程终结的时候调用notify_all()方法)
- A继续执行
join方法还可以传入一个大于0的数字,起到一个A线程让B线程先执行多长时间的一个作用。