關於Java線程Thread類的join方法的解釋

Thread類的join方法,Java官方文檔的解釋是:Waits for this thread to die.(等待線程死亡)。也就是程序會等待調用join方法的線程運行完,再執行當前線程,但不影響除這2個線程之外的線程的運行。這樣簡單的解釋可能很多同學並不是很理解,下面將詳細地解釋一些join方法。

首先我們先看一下join方法的代碼實現(JDK8):

public final void join() throws InterruptedException {
        join(0);
}


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

我們平時調用的無參的join方法最終會調用到這個帶一個參數的join方法,參數是0。看關鍵的第一個while循環的代碼,while會判斷調用此方法的線程是否是alive狀態(如果線程沒有運行完都是alive狀態,包括運行和阻塞),如果線程已經運行完,則方法結束,什麼也不做。如果線程沒有運行完,則調用調用此join方法線程的wait方法。這裏需要着重注意,此join方法是synchronized方法,也就是調用此方法的時,會在調用此方法的對象上加鎖。假設在B線程中調用的A線程對象的join方法,那麼就會在A線程對象上加鎖,這時調用wait方法時,注意這裏是在B線程中調用的A線程對象的wait方法,因此當前線程(B線程)就會處於等待狀態,並釋放A線程對象上的這把鎖。問題在於,我們在學習線程時知道,調用wait方法等待的線程需要等待其他線程調用notify或notifyAll方法並再此獲得這把鎖才能繼續運行,那爲什麼B線程wait以後,A線程執行完,B線程就又可以繼續執行了呢?原因在於,我們的鎖是加在A線程對象上的,當A線程運行結束之後,notify方法會被線程子系統調用(The notify() for this is handled by the Thread subsystem.),也就是說當線程運行結束後,線程對象上的notify方法會被調用。因此當A線程執行完,A線程對象就會調用notify方法,此時B線程就會被喚醒從而繼續運行。

下面我們通過具體的代碼來看一下:

public class Main {
    public static void main(String[] args) {

        final Thread threadA = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println(Thread.currentThread().getName() + " is running..." + i);
            }
        }, "A");
        threadA.start();


        final Thread threadB = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println(Thread.currentThread().getName() + " is running..." + i);
                if (i == 500) {
                    try {
                        threadA.join();     //調用threadA的join方法,相當於執行了下面註釋的代碼
                        // synchronized (threadA) {
                        //     while (threadA.isAlive()) {
                        //         threadA.wait(0);
                        //     }
                        // }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "B");
        threadB.start();

    }

}
我們創建並啓動了A和B兩個線程,在每個線程中分別打印當前線程的名字和循環次數,在B線程循環到500次時,調用A線程的join方法,注意,此時相當於相當於執行了join下面的註釋的代碼,然後B線程會等待,直到A線程運行結束以後,B線程纔會繼續運行。但由於join方法是在B線程中調用的,因此它並不會影響除A、B線程之外的其他線程的運行。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章