join方法是定义在Thread类里的一个final成员方法,因此需要一个Thread对象调用该方法,其语义为将调用join方法的线程对象加入到当前执行线程中先去执行。
先上源码
/**
* Waits for this thread to die.(等待直到调用该方法的线程死亡,即线程运行结束)
* ......(省略)
*/
public final void join() throws InterruptedException {
join(0);
}
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
* 当超时时间为0时意味着永远等待
* ......(省略)
*/
public final synchronized void join(long millis)
throws InterruptedException {
...(省略)
if (millis == 0) {
//主要逻辑部分(注意该方法是synchronized修饰的此时已经获取了调用该方法线程对象的锁)
//当this.isAlive()即当前调用join方法的线程是运行状态即调用了start方法
//那么调用this.wait(0);(即调用join方法的线程)
//会释放锁资源并在调用join方法的当前线程中等待
//此时释放锁资源之后
while (isAlive()) {
wait(0);
}
} else {
...
}
}
测试代码分析
public class JoinTest {
static Integer j = 0;
public static void main(String[] args) throws Exception {
Thread t = new Thread(()->{
for (int i = 0; i < 5; i++) {
j ++;
try {
Thread.sleep(200);
} catch (Exception e){
}
String name = Thread.currentThread().getName();
System.out.println(name + " : " + j);
}
},"t1");
Thread t2 = new Thread(()->{
for (int i = 0; i < 5; i++) {
j ++;
try {
Thread.sleep(300);
} catch (Exception e){
}
if (i > 0){
try {
t.join();
} catch (Exception e) {
e.printStackTrace();
}
}
String name = Thread.currentThread().getName();
System.out.println(name + " : " + j);
}
},"t2");
t.start();
t2.start();
Thread.sleep(1);
System.out.println(j);
}
}
顺序执行,当t.start()在sleep(200)处暂停,t2.start()在sleep(300)处暂停,最后一行打印2,
t中暂停时间先到打印"t1 : 2",再次循环并暂停,然后t2暂停时间到打印"t2 : 3 " 再次循环暂停在
sleep(300),t1再次打印,同时循环,t2暂停时间到,调用t.join()获取t的锁之后调用wait()方法
释放锁并在此处等待直到t1线程执行完成唤醒此处的wait
注:最终t1执行完的唤醒是notifyAll唤醒所有等待t1的线程,此处的wait只对t2起作用(即只是在当前线程中等待),对主线程或其他未调用t.join()的线程不会阻塞等待。且join只在线程运行过程中有效,当线程运行完,join的调用并未起到任何作用。