本篇来聊下线程合并-join:
概念
join字面上解释是加入/合并/连接的意思, 而api给出的解释:
/**
* Waits for this thread to die.
*/
在没有了解join方法功能前,“等当前线程死亡”,这解释有点蛋疼。这里先不解释,看案例:
public class App {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
//睡2s
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("这是子线程..." + Thread.currentThread().getName());
}
}, "t1");
//启动子线程
t1.start();
//t1.join();
//打印主线程
System.out.println("这是主线程....main");
}
}
运行后结果:在注释掉 t1.join() 方法时,打印结果
这是主线程....main
这是子线程...t1
如果解开t1.join() 方法注释时,打印结果
注意:t1.join必须在start调用后执行,否则没意义
这是子线程...t1
这是主线程....main
问题:2次操作,一个没有t1.join, 一个有t1.join, 打印的结果是相反的,为何?
答案:
join按照开篇的描述,其他都表示同一个意思,描述问题角度不一样而已。
合并:将t1线程合并到main线程中
main线程运行到t1.join方法时,会先阻塞,将cpu执行权交给t1,等待t1执行完毕,再获取CPU执行剩下的功能。居于这点,可以认为将t1线程执行的逻辑加入到main线程中执行,或者说将t1线程合并到main线程中执行。
等死:等待t1线程终止
跟合并解释一样,可以这么认为:main线程等t1线程执行完毕之后再执行。 也即等t1线程终止后再执行:Waits for this thread to die.
常见的api
thread.join(); //表示等待thread线程逻辑执行完毕 。
thread.join(1000); //表示等待thread线程1毫秒, 操作1毫秒,不再等待。
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;
}
}
}
分析
上面源码, join方法是synchronized修饰的方法, 当main线程调用 t1.join()进入join方法后,main线程马上获取到t1对象锁,接着进入到millis ==0这个分支, 执行wait(0) 方法, 当前线程也即主线程立马挂起, 在不被唤醒前提下, wait(0) 表示无线等待.
疑问
主线程调用wait(0), 已经阻塞了, 当t1线程执行完毕后, 为什么没有notify/notifyAll页面自动醒来,完成后续任务呢?
答案:其实线程执行完毕之后, jvm会线程进行收尾工作, 会唤醒该线程对象锁中阻塞的所有线程.如果想了解更加详细: 传送门