Thread类的join方法,Java官方文档的解释是:Waits for this thread to die.(等待线程死亡)。也就是程序会等待调用join方法的线程运行完,再执行当前线程,但不影响除这2个线程之外的线程的运行。这样简单的解释可能很多同学并不是很理解,下面将详细地解释一些join方法。
首先我们先看一下join方法的代码实现(JDK8):
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) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
我们平时调用的无参的join方法最终会调用到这个带一个参数的join方法,参数是0。看关键的第一个while循环的代码,while会判断调用此方法的线程是否是alive状态(如果线程没有运行完都是alive状态,包括运行和阻塞),如果线程已经运行完,则方法结束,什么也不做。如果线程没有运行完,则调用调用此join方法线程的wait方法。这里需要着重注意,此join方法是synchronized方法,也就是调用此方法的时,会在调用此方法的对象上加锁。假设在B线程中调用的A线程对象的join方法,那么就会在A线程对象上加锁,这时调用wait方法时,注意这里是在B线程中调用的A线程对象的wait方法,因此当前线程(B线程)就会处于等待状态,并释放A线程对象上的这把锁。问题在于,我们在学习线程时知道,调用wait方法等待的线程需要等待其他线程调用notify或notifyAll方法并再此获得这把锁才能继续运行,那为什么B线程wait以后,A线程执行完,B线程就又可以继续执行了呢?原因在于,我们的锁是加在A线程对象上的,当A线程运行结束之后,notify方法会被线程子系统调用(The notify() for this is handled by the Thread subsystem.),也就是说当线程运行结束后,线程对象上的notify方法会被调用。因此当A线程执行完,A线程对象就会调用notify方法,此时B线程就会被唤醒从而继续运行。
下面我们通过具体的代码来看一下:
public class Main {
public static void main(String[] args) {
final Thread threadA = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + " is running..." + i);
}
}, "A");
threadA.start();
final Thread threadB = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + " is running..." + i);
if (i == 500) {
try {
threadA.join(); //调用threadA的join方法,相当于执行了下面注释的代码
// synchronized (threadA) {
// while (threadA.isAlive()) {
// threadA.wait(0);
// }
// }
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "B");
threadB.start();
}
}
我们创建并启动了A和B两个线程,在每个线程中分别打印当前线程的名字和循环次数,在B线程循环到500次时,调用A线程的join方法,注意,此时相当于相当于执行了join下面的注释的代码,然后B线程会等待,直到A线程运行结束以后,B线程才会继续运行。但由于join方法是在B线程中调用的,因此它并不会影响除A、B线程之外的其他线程的运行。