Thread.join()方法原理分析

如果現在有這樣一個場景:在主線程中創建兩個線程T1和T2,通過start啓動線程後。我們想T1結束了再執行T2,等T1和T2都執行結束,才繼續執行主線程,怎麼能做到呢?
其實很簡單,通過join就可以實現:

public class JoinDemo {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 10000; i++){
                count ++;
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 10000; i++){
                count ++;
            }
        });
        t1.start();
        t1.join();
        t2.start();
        t2.join();
        System.out.println(count);
        for (int i = 0; i < 10000; i++){
            count ++;
        }
        System.out.println(count);
    }
}

像上面這樣,通過join就能實現讓主線程阻塞,等待T1執行完了再執行T2,T2完成再執行T1.

那到底是如何實現的呢?其實可以猜想一下,如果想讓主線程阻塞,等待某一個線程執行完成後再繼續執行,可以通過讓主線程Wait在某一個鎖上,等到子線程執行完成後,在喚醒主線程即可。那join是如何實現的呢?看join的源碼如下:

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確實是通過wait實現的,是以被等待的線程爲鎖對象,將主線程wait在以被等待線程爲鎖對象的對象鎖上。

有wait,必定會有notify,那在什麼時候notify呢?既然主線程是爲了等待被等待線程執行結束再繼續執行,那應該是在被等待線程退出的時候調用notify。所以,應該找找JVM線程退出時候的源碼:
在Thread.cpp中找到exit方法,會發現,在exit方法中調用了ensure_join方法,從註釋上可以看出,這個方法就是爲了在線程退出之前,喚醒wait在當前線程的對象鎖上的其他線程。
在這裏插入圖片描述
繼續看ensure_join方法的實現:

static void ensure_join(JavaThread* thread) {
   // We do not need to grap the Threads_lock, since we are operating on ourself.
   Handle threadObj(thread, thread->threadObj());
   assert(threadObj.not_null(), "java thread object must exist");
   ObjectLocker lock(threadObj, thread);
   // Ignore pending exception (ThreadDeath), since we are exiting anyway
   thread->clear_pending_exception();
   // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
   java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
   // Clear the native thread instance - this makes isAlive return false and allows the join()
   // to complete once we've done the notify_all below
   java_lang_Thread::set_thread(threadObj(), NULL);
   lock.notify_all(thread);
   // Ignore pending exception (ThreadDeath), since we are exiting anyway
   thread->clear_pending_exception();
 }

發現,最終確實是調用了notifyAll方法。

爲了加深對join的理解,其實我們爲了實現文章開頭的那個場景,並不需要調用join方法,只需要將主線程wait在被等待的線程的線程鎖對象上即可,同樣能實現和join相同的效果,因爲join就是這樣做的,所以我們可以這樣去寫(當然實際中肯定是用封裝好的join最簡單明瞭):

public class JoinDemo {
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        new JoinDemo().fun();
    }

    private void fun() throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 10000; i++){
                count ++;
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 10000; i++){
                count ++;
            }
        });
        t1.start();
        synchronized(t1){
            t1.wait();
        }
//        t1.join();
        t2.start();
        synchronized (t2){
            t2.wait();
        }
//        t2.join();
        System.out.println(count);
        for (int i = 0; i < 10000; i++){
            count ++;
        }
        System.out.println(count);
    }
}

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