join代码演示
import java.util.ArrayList;
import java.util.List;
public class JoinTest {
public static void main(String[] args) {
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 50; i++) {
threads.add(new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " running...");
}, "" + i));
}
threads.forEach(t -> {
t.start();
try {
// 主线程等待所有的t运行完毕后才会执行,循环也会卡在t.join处,等到join结束才会继续下一次循环
t.join();
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
});
System.out.println("run over!");
}
}
正如注释所写,主线程会等待另外50个子线程运行完毕之后才会运行,而每一个子线程也会等待上一个子线程运行完毕之后才会运行。这是因为:
join()方法调用的是join(millis)方法, join(millis)方法加了同步关键字,主线程会持有对象t的锁,然后 (1)若join(millis)方法的参数是0,则主线程会无限等待下去,直到子线程退出时将其唤醒。主线程循环判断t的isAlive方法的返回值是否为true(t在start之后、消亡之前都是true),若为true则调用wait(0)一直等待下去,直到线程消亡时调用notifyAll方法通知主线程。若中间被意外唤醒,由于isAlive方法是true,因此还会继续阻塞 (2)若join(millis)方法的参数大于0,主线程也会循环判断t的isAlive方法的返回值是否为true,是true则调用wait(millis)等待一段时间,等到这段时间之后就会退出等待,或者线程消亡时通过notifyAll方法唤醒wait方法,这时isAlive会返回false,然后退出。若中间被意外唤醒,由于isAlive方法是true,因此还会继续阻塞,并会重新计算阻塞的时间
join必须在start之后调用,否则t的isAlive方法会返回flase,主线程不会阻塞
join(millis)方法源码
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0L;
if (millis < 0L) {
throw new IllegalArgumentException("timeout value is negative");
} else {
if (millis == 0L) {
while(this.isAlive()) {
this.wait(0L);
}
} else {
while(this.isAlive()) {
long delay = millis - now;
if (delay <= 0L) {
break;
}
this.wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
}