在面试中经常会遇到这样的问题:在主线程中有两个子线程,如果能让着两个子线程能顺序的执行?
答案自然是用 join 来使得两个线程顺序执行,先看一下具体代码
public class ThreadOfJoin {public static void main(String[] args) throws Exception {MyThread luck = new MyThread("Luck");
MyThread timi = new MyThread("Timi");
luck.start();
luck.join();
timi.start();}
@Data
static class MyThread extends Thread {
private String userName;
public MyThread(String userName) {this.userName = userName;}
@Override
public void run() {
try {for (int i = 0; i < 5; i++) {System.out.println(userName + "-" + i);
Thread.sleep(1000);
}
} catch (Exception e) {e.printStackTrace();
}
}
}
}
每个线程启动后,打印五次信息,通过不同的名字来区分是哪个线程打印的。执行结果如下
Luck - 0
Luck - 1
Luck - 2
Luck - 3
Luck - 4
Timi - 0
Timi - 1
Timi - 2
Timi - 3
Timi - 4
通过结果可以看到 join 可以使得两个线程是顺序执行,那为什么 join 能控制线程顺序执行呢,我们看下 join 的具体实现
// 外部调用的方法
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 通过 synchronized 关键字来保证线程安全,主线程在调用了 luck.start()之后调用了 luck.join(),当 luck 线程未执行完成是,主线程会被以下代码阻塞
if (millis == 0) {//join()方法默认 milis 为 0
while (isAlive()) {// 线程未执行完成,此条件为 true
wait(0);// 等待 notify
}
}
当 luck 线程执行完成之后,此线程的生命周期即将结束,在生命周期结束前,luck 线程会使用 notifyAll()方法,通知所有正在等待该对象锁的线程 (我即将死去,你们不要再等了)。wait(0) 接收到 notify 之后,会再次进行 isAlive()判断,luck 死亡之后,就跳出循环,join 方法结束,之后就继续执行主线程中的其他代码。
同时我们也能看到 join 方法里面能传递时间参数,大概作用就是等待指定时间之后,如果之前线程还未执行完成,那么久不再等待。