最近在看面試題遇到一個問題描述如下:
有線程 T1、T2 和 T3。你如何確保 T2 線程在 T1 之後執行,並且 T3 線程在 T2 之後執行?
剛看到這個問題時我認爲,想讓T1->T2->T3依次執行,那就依次定義這樣三個線程並按這個順序啓動就可以了嘛!
後來想想是我天真了,其實題目的要求應該是"T2在T1結束後開始執行,T3在T2執行結束後開始執行"
依據測試人員的思想,假如三個線程的執行時間爲T3<T2<T1時呢?
再按照我最初的想法,可以三個線程執行順序就亂套了!
仔細研究後發現這個題目的本質是考察Thread類的join方法的........
下面直接上代碼了↓↓↓
public class Test {
public static void main(String[] args) {
Thread ta = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("線程A開始執行.....");
Thread.sleep(8000);
System.out.println("線程A執行結束.....");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread tb = new Thread(new Runnable() {
@Override
public void run() {
try {
ta.join();//等待線程A執行結束
System.out.println("線程B開始執行.....");
Thread.sleep(4000);
System.out.println("線程B執行結束.....");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread tc = new Thread(new Runnable() {
@Override
public void run() {
try {
tb.join();//等待線程B執行結束
System.out.println("線程C開始執行.....");
Thread.sleep(1000);
System.out.println("線程C執行結束.....");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
ta.start();//線程A開始執行
tb.start();//線程B開始執行
tc.start();//線程C開始執行
}
}
執行結果如下:
Thread類中join方法的解析:
//方法被synchronized,鎖爲this,this就是調用join的對象
public final synchronized void join(long millis)
throws InterruptedException {
//獲取啓動時刻的時間戳
long base = System.currentTimeMillis();
//當前時間的初始化
long now = 0;
//①參數小於0,非法情況拋出異常
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
//②參數等於0,意爲着無限等待
if (millis == 0) {
//判斷當前線程是否狀態,線程若未運行(未啓動/已終止),則沒有必要繼續等待了
while (isAlive()) {
wait(0);
}
} else {//③參數大於0
//判斷當前線程是否狀態,線程若未運行(未啓動/已終止),則沒有必要繼續等待了
while (isAlive()) {
//計算出還需等待的時間
long delay = millis - now;
//若等待的時間小於0,說明等待時間間隔期已滿,結束循環
if (delay <= 0) {
break;
}
//執行線程等待
wait(delay);
//計算出線程已等待時間
now = System.currentTimeMillis() - base;
}
}
}
綜合分析上面面試題的案例:
①線程tb中執行了ta.join(),那麼此時的調用線程爲tb,調用對象爲ta.
②對象ta充當了同步鎖,tb線程會進入等待狀態.當ta執行結束後,tb才被喚醒
③同理,線程tc中執行了tb.join(),那麼此時的調用線程爲tc,調用對象爲tb.
④對象tb充當了同步鎖,tc線程會進入等待狀態.當tb執行結束後,tc才被喚醒