Java Thread的join() 原理

join

線程的合并的含義就是 將幾個并行線程的線程合并為一個單線程執(zhí)行,應(yīng)用場景是 當(dāng)一個線程必須等待另一個線程執(zhí)行完畢才能執(zhí)行時,Thread類提供了join方法來完成這個功能,注意,它不是靜態(tài)方法。

join有3個重載的方法:

void join():當(dāng)前線程等該加入該線程后面,等待該線程終止。
void join(long millis):當(dāng)前線程等待該線程終止的時間最長為 millis 毫秒。 如果在millis時間內(nèi),該線程沒有執(zhí)行完,那么當(dāng)前線程進(jìn)入就緒狀態(tài),重新等待cpu調(diào)度。
void join(long millis,int nanos):等待該線程終止的時間最長為 millis 毫秒 + nanos納秒。如果在millis時間內(nèi),該線程沒有執(zhí)行完,那么當(dāng)前線程進(jìn)入就緒狀態(tài),重新等待cpu調(diào)度。

我們來看個demo:
新建一個Thread類,重寫run()方法:

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("子線程執(zhí)行完畢");
    }
}

新建測試類,測試Join()方法:

public class TestThread {

    public static void main(String[] args) {
        //循環(huán)五次
        for (int i = 0; i < 5; i++) {

            MyThread thread = new MyThread();
            //啟動線程
            thread.start();
            try {
                //調(diào)用join()方法
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主線程執(zhí)行完畢");
            System.out.println("~~~~~~~~~~~~~~~");

        }
    }
}

子線程執(zhí)行完畢
主線程執(zhí)行完畢
~~~~~~~~~~~~~~~
子線程執(zhí)行完畢
主線程執(zhí)行完畢
~~~~~~~~~~~~~~~
子線程執(zhí)行完畢
主線程執(zhí)行完畢
~~~~~~~~~~~~~~~
子線程執(zhí)行完畢
主線程執(zhí)行完畢
~~~~~~~~~~~~~~~
子線程執(zhí)行完畢
主線程執(zhí)行完畢
~~~~~~~~~~~~~~~

結(jié)果分析: 子線程每次都在主線程之前執(zhí)行完畢.

源碼解讀

public final void join() throws InterruptedException {
    //當(dāng)調(diào)用join()時,實際是調(diào)用join(long)方法
        join(0);
    }

查看Join(long)方法源碼:

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) {  //由于上一步傳入?yún)?shù)為0,因此調(diào)用當(dāng)前判斷
            while (isAlive()) { //判斷子線程是否存活
                wait(0); //調(diào)用wait(0)方法
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

/**
     * Tests if this thread is alive. A thread is alive if it has
     * been started and has not yet died.
     * 測試線程是否還活著。如果線程存活的話它就是已經(jīng)開始,還沒有死亡的狀態(tài)。
     * @return  <code>true</code> if this thread is alive;
     *          <code>false</code> otherwise.
     */****
    public final native boolean isAlive();

該方法為本地方法,判斷線程對象是否存活,若線程對象調(diào)用了start()方法,在沒有死亡的情況下此判斷為true。

A thread is alive if it has been started and has not yet died. A thread "starts" when its start() method is invoked and "dies" at the end of its run() method, or when stop() (now deprecated) is invoked. So yes, a thread is "alive" when its run() method is still ongoing, but it is also "alive" in the time window between the invocation of start() and the implicit invocation of the run() method by the JVM.

查看wait()方法源碼:

public final native void wait(long timeout) throws InterruptedException;

該方法為本地方法,調(diào)用此方法的當(dāng)前線程需要釋放鎖,并等待喚醒。在上述例子中,主線程調(diào)用子線程對象的join()方法,因此主線程在此位置需要釋放鎖,并進(jìn)行等待。

我們來小結(jié)下上述步驟: 主線程wait()等待,子線程調(diào)用了run()執(zhí)行,打印“子線程執(zhí)行完畢”。此時,主線程還沒被喚醒,還沒有執(zhí)行下面的操作。那么,問題來了,誰?在什么時候?喚醒了主線程呢?
在stackoverflow上 找到
The notify() for this is handled by the Thread subsystem. When the run() method finishes, the notify() is called on the Thread object. I'm not sure if the code that actually calls notify() can be seen -- it seems to be done in native code.

No user code needs to call notify() on that Thread object. The Java Thread code handles this internally. Once the thread finishes, the join() call will return.

As for jdk7 for linux, you can get the answer from the source code of openjdk.

/jdk7/hotspot/src/os/linux/vm/os_linux.cpp

int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

static void *java_start(Thread *thread) {
  ...
  thread->run();
  return 0;
}

and when start thread in java, the thread will be instanceof JavaThread.

/jdk7/hotspot/src/share/vm/runtime/thread.cpp

void JavaThread::run() {
  ...
  thread_main_inner();
}

void JavaThread::thread_main_inner() {
  ...
  this->exit(false);
  delete this;
}

void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
  ...
  // Notify waiters on thread object. This has to be done after exit() is called
  // on the thread (if the thread is the last thread in a daemon ThreadGroup the
  // group should have the destroyed bit set before waiters are notified).
  ensure_join(this);
  ...
}

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

so lock.notify_all(thread) will notify all threads whose wait for the thread to finish.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容