要保證T1、T2、T3三個線程順序執行,可以利用Thread類的join方法。
join方法是synchronized,所以需要獲取Thread的對象鎖才能進入,只有獲得了鎖才能調用wait放棄對鎖的獨占並等待再次獲取鎖。
join方法用線程對象調用,如果在一個線程A中調用另一個線程B的join方法,線程A將會等待線程B執行完畢后再執行。
join 方法是一個阻塞方法,用來進行線程之間的交流。線程 A 調用 線程 B 的 join 方法,則線程 A 將阻塞,線程 B 執行結束后 線程 A 開始執行。
問:join方法的作用?
答: Thread類中的join方法的主要作用就是同步,它可以使得線程之間的並行執行變為串行執行。當我們調用某個線程的這個方法時,這個方法會掛起調用線程,直到被調用線程結束執行,調用線程才會繼續執行。
問:現在有T1、T2、T3三個線程,你怎樣保證T2在T1執行完后執行,T3在T2執行完后執行?
答:要保證T1、T2、T3三個線程順序執行,可以利用Thread類的join方法。
問:join方法的作用?
答: Thread類中的join方法的主要作用就是同步,它可以使得線程之間的並行執行變為串行執行。當我們調用某個線程的這個方法時,這個方法會掛起調用線程,直到被調用線程結束執行,調用線程才會繼續執行。
問:join方法傳參和不傳參的區別?
答:join方法中如果傳入參數,則表示這樣的意思:如果A線程中掉用B線程的join(10),則表示A線程會等待B線程執行10毫秒,10毫秒過后,A、B線程並行執行。需要注意的是,jdk規定,join(0)的意思不是A線程等待B線程0秒,而是A線程等待B線程無限時間,直到B線程執行完畢,即join(0)等價於join()。
代碼如下:
/*
-
本測試程序主要是測試join方法的傳參與不傳參的區別
-
*/
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ThreadJoinTest t1 = new ThreadJoinTest(“線程2017春末”);
ThreadJoinTest t2 = new ThreadJoinTest(“線程2019夏初”);
t1.start();
/**join方法可以傳遞參數,join(10)表示main線程會等待t1線程10毫秒,10毫秒過去后,
* main線程和t1線程之間執行順序由串行執行變為普通的並行執行
*/
t1.join(5);
t2.start();}
static class ThreadJoinTest extends Thread {
public ThreadJoinTest(String name) {
super(name);
}@Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(this.getName() + ":" + i); } }}
}
運行結果:我們可以發現線程t1還沒執行完。線程t2就開始執行了。

當使用join()無參方法時:可以看到t2會等到t1執行完畢才開始執行。

問:join與start調用順序問題
答:join方法必須在線程start方法調用之后調用才有意義。這個也很容易理解:如果一個線程都沒有start,那它也就無法同步了。因為執行完start方法才會創建線程。
問:join方法實現原理
答:join方法是通過調用線程的wait方法來達到同步的目的的。例如A線程中調用了B線程的join方法,則相當於在A線程中調用了B線程的wait方法,當B線程執行完(或者到達等待時間),B線程會自動調用自身的notifyAll方法喚醒A線程,從而達到同步的目的。
問:源碼分析join方法
isAlive()判斷線程是否還活着,即線程是否還未終止。
答:由下面的join方法源碼可以看到:
1、如果join方法傳參為0的話,則會調用isAlive()方法,一直檢測線程是否存活(執行完畢),如果存活就調用wait方法,一直阻塞。
2、如果參數為負數,則直接報錯:“timeout value is negative”
3、如果參數大於0,則while里面一直判斷線程是否存活,存活的話就一直判斷當前線程執行的時間並與計算還需要等待多久時間,最后如果等待時間小於等於0就跳出循環,否則就繼續wait
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;
}
}
}
問:回歸到題目,手寫一個確保T1、T2、T3的執行順序的代碼
package com.threadDemo;
public class JoinTestSync {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ThreadJoinTest1 t1 = new ThreadJoinTest1("今天");
ThreadJoinTest1 t2 = new ThreadJoinTest1("明天");
ThreadJoinTest1 t3 = new ThreadJoinTest1("后天");
/*
* 通過join方法來確保t1、t2、t3的執行順序
* */
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();
}
}
class ThreadJoinTest1 extends Thread{
public ThreadJoinTest1(String name){
super(name);
}
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.println(this.getName() + “:” + i);
}
}
}
執行結果如下所示:的確是順序執行的。

也有些同學在剛開始學習的時候會采用如下的方式,再次我也進行驗證下結論:

執行結果:

發現並不是按照t1、t2、t3的執行順序的,所以必須是按照以下順序
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();
至此,關於多個線程的順序執行問題講解完畢,感謝閱讀!
