join()方法實例演示及源碼分析

join()方法實例演示及源碼分析

以下爲測試代碼,下文圍繞該代碼展開:

public class MyThread1 implements Runnable {
    public void run() {
        System.out.printf("Beginning MyThread1: %s\n", new Date());
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("MyThread1 has finished: %s\n", new Date());
    }
}

public class MyThread2 implements Runnable {
	public void run() {
        System.out.printf("Beginning MyThread2: %s\n", new Date());
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("MyThread2 has finished: %s\n", new Date());
    }

    public static void main(String[] args) {
        MyThread1 myThread1 = new MyThread1 ();
        Thread thread1 = new Thread(myThread1, "MyThread1");

        MyThread2 myThread2 = new MyThread2 ();
        Thread thread2 = new Thread(myThread2, "MyThread2 ");
        
		thread1.start();
        thread2.start();
		try {
            thread1.join();
            thread2.join(1800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.printf("Main: Configuration has been loaded: %s\n", new Date());
    }
}

分析:thread1.start(); thread2.start(); 兩個線程開啓,搶佔CPU資源順序無法確定,所以第一句執行的語句無法確定。

繼續 thread1.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;
            }
        }
    }

主要分析:

public final synchronized void join(long millis)
    throws InterruptedException {
        while (isAlive()) {
            wait(0);
        }

主線程獲取join()方法的鎖,判斷thread1線程是否存活,若存活則則調用wait()方法,wait()方法只會讓持有鎖的線程進入等待,因此主線程阻塞,進入等待(join方法會造成當前線程wait,就如你看到的這裏的wait(0),是當前線程wait,並不是調用者wait)。所以2s之後thread1的結束線程語句打印,主線程被喚醒,繼續執行thread2.join(1800); 主線程繼續阻塞,當過了1.8s之後,thread2依然沒有執行完畢,從源碼可知,此時會break,主線程被喚醒,繼續執行完主線程結束語句,最後纔會執行thread2執行結束語句。

打印結果:

Beginning MyThread1: Thu Jul 11 17:04:37 CST 2019
Beginning MyThread2: Thu Jul 11 17:04:37 CST 2019
MyThread1 has finished: Thu Jul 11 17:04:39 CST 2019
Main: Configuration has been loaded: Thu Jul 11 17:04:40 CST 2019
MyThread2 has finished: Thu Jul 11 17:04:41 CST 2019

或者:

Beginning MyThread2: Thu Jul 11 17:05:18 CST 2019
Beginning MyThread1: Thu Jul 11 17:05:18 CST 2019
MyThread1 has finished: Thu Jul 11 17:05:20 CST 2019
Main: Configuration has been loaded: Thu Jul 11 17:05:22 CST 2019
MyThread2 has finished: Thu Jul 11 17:05:22 CST 2019

至於thread1執行完畢,主線程被喚醒,但是從上面上源代碼裏並沒有看到notify調用,而且文檔上表明不建議應用程序自己去調用,最終答案在jvm C++源碼裏,源碼之下:

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

重點看ensure_join 方法裏的 lock.notify_all(thread);語句代碼說明jvm會在線程B執行完成後幫我們調用notify_all()方法,這樣主線程就會重回Runnable狀態,時間片分配後主線程可執行join方法之後的代碼。

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