在面試中經常會遇到這樣的問題:在主線程中有兩個子線程,如果能讓着兩個子線程能順序的執行?
答案自然是用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方法裏面能傳遞時間參數,大概作用就是等待指定時間之後,如果之前線程還未執行完成,那麼久不再等待。