Thread之三:Thread Join()的用法

 1、join用法

join()和wait()不會釋放鎖,join()是Thread的方法,wait()是Object的方法oop

 1.join方法定義在Thread類中,則調用者必須是一個線程this

例如:spa

Thread t = new CustomThread();//這裏通常是自定義的線程類
t.start();//線程起動
t.join();//此處會拋出InterruptedException異常

2.上面的兩行代碼也是在一個線程裏面執行的線程

以上出現了兩個線程,一個是咱們自定義的線程類,咱們實現了run方法,作一些咱們須要的工做;另一個線程,生成咱們自定義線程類的對象,而後執行code

customThread.start();
customThread.join();

在這種狀況下,兩個線程的關係是一個線程由另一個線程生成並起動,因此咱們暫且認爲第一個線程叫作「子線程」,另一個線程叫作「主線程」。對象

2、爲何要用join()方法blog

主線程生成並啓動了子線程,而子線程裏要進行大量的耗時的運算(這裏能夠借鑑下線程的做用),當主線程處理完其餘的事務後,須要用到子線程的處理結果,這個時候就要用到join();方法了。事務

3、join方法的做用get

在網上看到有人說「將兩個線程合併」。這樣解釋我以爲理解起來還更麻煩。不如就借鑑下API裏的說法:源碼

「等待該線程終止。」

解釋一下,是主線程等待子線程的終止。也就是說主線程的代碼塊中,若是碰到了t.join()方法,此時主線程須要等待(阻塞),等待子線程結束了(Waits for this thread to die.),才能繼續執行t.join()以後的代碼塊。

4、示例

class BThread extends Thread {
    public BThread() {
        super("[BThread] Thread");
    };
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println(threadName + " loop at " + i);
                Thread.sleep(1000);
            }
            System.out.println(threadName + " end.");
        } catch (Exception e) {
            System.out.println("Exception from " + threadName + ".run");
        }
    }
}
class AThread extends Thread {
    BThread bt;
    public AThread(BThread bt) {
        super("[AThread] Thread");
        this.bt = bt;
    }
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        try {
            bt.join();
            System.out.println(threadName + " end.");
        } catch (Exception e) {
            System.out.println("Exception from " + threadName + ".run");
        }
    }
}
public class TestDemo {
    public static void main(String[] args) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        BThread bt = new BThread();
        AThread at = new AThread(bt);
        try {
            bt.start();
            Thread.sleep(2000);
            at.start();
            at.join();
        } catch (Exception e) {
            System.out.println("Exception from main");
        }
        System.out.println(threadName + " end!");
    }
}

結果:

main start.    //主線程起動,由於調用了at.join(),要等到at結束了,此線程才能向下執行。 
[BThread] Thread start. 
[BThread] Thread loop at 0 
[BThread] Thread loop at 1 
[AThread] Thread start.    //線程at啓動,由於調用bt.join(),等到bt結束了才向下執行。 
[BThread] Thread loop at 2 
[BThread] Thread loop at 3 
[BThread] Thread loop at 4 
[BThread] Thread end. 
[AThread] Thread end.    // 線程AThread在bt.join();阻塞處啓動,向下繼續執行的結果 
main end!      //線程AThread結束,此線程在at.join();阻塞處啓起動,向下繼續執行的結果。

修改一下代碼:

public class TestDemo {
    public static void main(String[] args) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " start.");
        BThread bt = new BThread();
        AThread at = new AThread(bt);
        try {
            bt.start();
            Thread.sleep(2000);
            at.start();
            //at.join(); //在此處註釋掉對join()的調用
        } catch (Exception e) {
            System.out.println("Exception from main");
        }
        System.out.println(threadName + " end!");
    }
}

結果:

main start.    // 主線程起動,由於Thread.sleep(2000),主線程沒有立刻結束;

[BThread] Thread start.    //線程BThread起動
[BThread] Thread loop at 0
[BThread] Thread loop at 1
main end!   // 在sleep兩秒後主線程結束,AThread執行的bt.join();並不會影響到主線程。
[AThread] Thread start.    //線程at起動,由於調用了bt.join(),等到bt結束了,此線程才向下執行。
[BThread] Thread loop at 2
[BThread] Thread loop at 3
[BThread] Thread loop at 4
[BThread] Thread end.    //線程BThread結束了
[AThread] Thread end.    // 線程AThread在bt.join();阻塞處起動,向下繼續執行的結果

5、從源碼看join()方法

在AThread的run方法裏,執行了bt.join();,進入看一下它的JDK源碼:

public final void join() throws InterruptedException {
    join(0L);
}
    public final synchronized void join(long l)
        throws InterruptedException
    {
        long l1 = System.currentTimeMillis();
        long l2 = 0L;
        if(l < 0L)
            throw new IllegalArgumentException("timeout value is negative");
        if(l == 0L)
            for(; isAlive(); wait(0L));
        else
            do
            {
                if(!isAlive())
                    break;
                long l3 = l - l2;
                if(l3 <= 0L)
                    break;
                wait(l3);
                l2 = System.currentTimeMillis() - l1;
            } while(true);
    }

Join方法實現是經過wait(小提示:Object 提供的方法)。 當main線程調用t.join時候,main線程會得到線程對象t的鎖(wait 意味着拿到該對象的鎖),調用該對象的wait(等待時間),直到該對象喚醒main線程 ,好比退出後。這就意味着main 線程調用t.join時,必須可以拿到線程t對象的鎖。

相關文章
相關標籤/搜索