一篇文章帶你徹底搞懂join的用法

千鋒Python唐小強發表於2020-07-15

java多執行緒裡的join,從字面意思來看是聯合,合併的意思,但如果面試時這麼回答,基本上可以斷定面試者還沒搞懂。join究竟能幹什麼,今天給出一個最通俗的解釋,那就是在多執行緒環境下實現暫時以單執行緒執行,或者說在並行執行的環境中實現暫時以序列執行。為了說明這個問題,我們看一段再常見不過的程式碼,程式碼內容是,讓三個執行緒分佈去列印一段內容

//程式碼塊
1

public class TestJoin {
   public static void main(String[] args) throws InterruptedException {
       Thread t1 = new Thread(new DoSth());
       Thread t2 = new Thread(new DoSth());
       Thread t3 = new Thread(new DoSth());
       t1.start();
       t2.start();
       t3.start();
       System.out.println( "主執行緒執行");
   }
}

class DoSth implements Runnable {
   @Override
   public void run() {
        int n = 5;
        while (n > 0) {
           System.out.println(Thread.currentThread().getName());
           try {
               Thread.sleep( 1);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           n--;
       }
   }
}

執行結果如下:

執行緒1執行

執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒1執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒1執行
執行緒1執行
執行緒1執行

可見三個執行緒各自並行執行,並無明確的先後順序。但如果我們在t.start()後面加上這行程式碼,


//程式碼塊2

t1.start();
t1.join();

看會出現看什麼樣的結果:

執行緒1執行

執行緒1執行
執行緒1執行
執行緒1執行
執行緒1執行
執行緒2執行
執行緒3執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒2執行

可以看到執行緒1執行結束之後執行緒2和3才開始執行,可見線上程1執行過程中,其他執行緒並未執行,執行緒1結束後,執行緒2,執行緒3開始並行執行,這就印證了前面的結論,即:join的作用是在多執行緒環境下暫時以單執行緒執行。明白了這一點,接下來的問題是,這個特性是怎麼實現的呢?我們跟到原始碼:可以看到

//程式碼塊3

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;
   }
}

}

在第12行呼叫了wait,注意這裡的wait,它並不是指執行緒執行緒1物件執行wait,而是執行緒1的呼叫者,也就是相當於在主執行緒去執行wait,可等價理解為以下虛擬碼:


t1
.start();

while(t1.isAlive()){
    Thread .currentThread() .doWait()
}

此時執行流程會在程式碼塊3的11-13行迴圈執行,當執行緒1執行完畢時,其生命週期結束,isAlive()返回false,11-13行退出迴圈,繼續執行下面的程式碼,此時又切換為並行執行狀態。對於以上執行效果,我們完全可以不用建立t1執行緒,而是把在主執行緒中直接去呼叫t1的核心邏輯,程式碼如下:

public static void main(String[] args) throws InterruptedException {

    //Thread t1 = new Thread( new DoSth(), "執行緒1");
   Thread t2 = new Thread( new DoSth(), "執行緒2");
   Thread t3 = new Thread( new DoSth(), "執行緒3");
    //t1.start();
    //t1.join();
    new DoSth().run(); //直接呼叫業務邏輯,而不是分配執行緒去執行
   t2.start();
   t3.start();
   System.out.println( "主執行緒執行");
}

和之前的程式碼相比,本來需要在子執行緒t1中執行的內容,透過在主執行緒中執行達到了相同的效果,而這種特性,就體現了所謂的join,現在你明白為什麼叫join了吧?join在實際應用當中有什麼用呢?把以上程式碼改造一下,用一個例子來說明。



public 
static 
void 
main(
String[] args) throws InterruptedException {

   Thread t1 = new Thread( new DoSth(), "小組1");
   Thread t2 = new Thread( new DoSth(), "小組2");
   Thread t3 = new Thread( new DoSth(), "小組3");
   t1.start();
   t2.start();
   t3.start();
   t1. join();
   t2. join();
   t3. join();
   System. out.println( "集合完畢");
}

執行結果:

小組3正在集合

小組1正在集合
小組2正在集合
小組3正在集合
小組1正在集合
小組2正在集合
小組3正在集合
小組1正在集合
集合完畢

當我們需要多個子執行緒分佈去完成各自的任務,並在這些子執行緒全部完成後,主執行緒做統一彙總時,join就派上用場了。不過細心的讀者會發現,這些子執行緒並未返回任何結果,如果我們需要返回結果供主執行緒使用時,該怎麼實現,針對這一需求,單靠實現Runnable的方式已經無法做到了,此時需要用到另外的介面:Feature和Callable。

一篇文章帶你徹底搞懂join的用法


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69923331/viewspace-2704761/,如需轉載,請註明出處,否則將追究法律責任。

相關文章