多線程中join()方法的分析

現在有 T1、T2、T3 三個線程,你怎樣保證 T2 在 T1 執行完後執行,T3 在 T2 執行完後執 行?

      這個線程問題通常會在第一輪或電話面試階段被問到,目的是檢測你對”join”方法是否熟 悉。這個多線程問題比較簡單,可以用 join 方法實現。

 

首先給出結論:t.join()方法只會使主線程進入等待池並等待t線程執行完畢後纔會被喚醒。並不影響同一時刻處在運行狀態的其他線程。

下面則是分析過程。

之前對於join()方法只是瞭解它能夠使得t.join()中的t優先執行,當t執行完後纔會執行其他線程。能夠使得線程之間的並行執行變成串行執行。


public class TestJoin {
 
    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        ThreadTest t1=new ThreadTest("A");
        ThreadTest t2=new ThreadTest("B");
        t1.start();
        t2.start();
    }
 
 
}
class ThreadTest extends Thread {
    private String name;
    public ThreadTest(String name){
        this.name=name;
    }
    public void run(){
        for(int i=1;i<=5;i++){
                System.out.println(name+"-"+i);
        }        
    }
}
運行結果:

A-1
B-1
B-2
B-3
A-2
B-4
A-3
B-5
A-4
A-5
可以看出A線程和B線程是交替執行的。

而在其中加入join()方法後(後面的代碼都略去了ThreadTest類的定義)







public class TestJoin {
 
    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        ThreadTest t1=new ThreadTest("A");
        ThreadTest t2=new ThreadTest("B");
        t1.start();
        t1.join();
        t2.start();
    }
}
運行結果:

A-1
A-2
A-3
A-4
A-5
B-1
B-2
B-3
B-4
B-5


顯然,使用t1.join()之後,B線程需要等A線程執行完畢之後才能執行。需要注意的是,t1.join()需要等t1.start()執行之後執行纔有效果,此外,如果t1.join()放在t2.start()之後的話,仍然會是交替執行,然而並不是沒有效果,這點困擾了我很久,也沒在別的博客裏看到過。

爲了深入理解,我們先看一下join()的源碼。

    /**
     * Waits for this thread to die.
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join() throws InterruptedException {
        join(0);            //join()等同於join(0)
    }
    /**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    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);           //join(0)等同於wait(0),即wait無限時間直到被notify
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }


可以看出,join()方法的底層是利用wait()方法實現的。可以看出,join方法是一個同步方法,當主線程調用t1.join()方法時,主線程先獲得了t1對象的鎖,隨後進入方法,調用了t1對象的wait()方法,使主線程進入了t1對象的等待池,此時,A線程則還在執行,並且隨後的t2.start()還沒被執行,因此,B線程也還沒開始。等到A線程執行完畢之後,主線程繼續執行,走到了t2.start(),B線程纔會開始執行。

PS:join源碼中,只會調用wait方法,並沒有在結束時調用notify,這是因爲線程在die的時候會自動調用自身的notifyAll方法,來釋放所有的資源和鎖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章