Java Thread.join()方法

join方法只有在繼承了Thread類的線程中才有。java

線程必需要start() 後再join才能起做用。oop

1. 使用方式

join是Thread類的一個方法,啓動線程後直接調用。this

2. 爲何使用join方法

在不少狀況下,主線程生成並啓動了子線程,若是子線要進行大量的耗時運算,主線程每每先於子線程結束,可是若是主線程處理完其餘的事務後,須要用到子線程的處理結果,也就是主線程須要等待子線程執行完以後再結束,這個時候就用join方法。spa

換句話說將另一個線程join到當前線程,則須要等到join進來的線程執行完纔會繼續執行當前線程。join來者是客,讓客人先執行完線程

3. join方法的做用

在JDK文檔裏,JAVA對join方法的描述是:code

join 

public final void join() throws InterruptedException Waits for this thread to die. Throws: InterruptedException  - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.對象

也就是說,join的做用是等待該線程終止,這裏須要理解的就是該線程是指主線程等待子線程的終止。也就是在子線程調用了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);//確保bt線程啓動
            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);
}

而後進入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);
}

單純從代碼上看:

* 若是線程被生成了,但還未被起動,isAlive()將返回false,調用它的join()方法是沒有做用的。將直接繼續向下執行。

* 在AThread類中的run方法中,bt.join()是判斷bt的active狀態,若是bt的isActive()方法返回false,在bt.join(),這一點就不用阻塞了,能夠繼續向下進行了。從源碼裏看,wait方法中有參數,也就是不用喚醒誰,只是再也不執行wait,向下繼續執行而已。

* 在join()方法中,對於isAlive()和wait()方法的做用對象是個比較讓人困惑的問題: 

isAlive()方法的簽名是:public final native boolean isAlive(),也就是說isAlive()是判斷當前線程的狀態,也就是bt的狀態。

相關文章
相關標籤/搜索