多線程下的wait為什麼可以不需要notify
- 2019 年 10 月 15 日
- 筆記
多線程下的wait方法就像我無處安放的青春,胡亂來,感覺沒有一點套路。wait後不需要notify仍可以繼續執行。所以我決定看看到底咋回事。。。。。
先結合join方法了解一下。
join方法是可以等待其它線程執行完成的方法。就像Main線程需要等待A、B執行完畢,只需要執行a.join(),b.join()即可,主線程會阻塞等待A、B線程執行完畢。
join源碼:
public final void join() throws InterruptedException { join(0); }
發現其使用的是join(long millis)
即:
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; } } }
可見,其實現使用的是wait方法。使用wait方法阻塞當前線程。此時就產生了一個很尷尬的問題,join內部使用wait後並沒有notify或notifyAll,線程不會一直阻塞嗎?
進入正題
查看一個wait列子:
public class WaitTest { public static void main(String[] args) throws InterruptedException { Thread b = new B(); new WaitTest().test(b); } public void test(Thread b) throws InterruptedException { synchronized (b) { b.start(); System.out.println("主方法開始執行"); b.wait(); System.out.println("主方法執行完畢"); } } } class B extends Thread { @Override public void run() { synchronized (this) { System.out.println("開始執行線程B"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("B線程執行完畢"); } } }
執行結果:
主方法開始執行 開始執行線程B B線程執行完畢 主方法執行完畢
可以見到主線程正常執行完畢了。十分疑惑了,從小老師就告訴我wait需要notify或notifyAll喚醒,咋滴多線程情況下膨脹了,不聽使喚了,一個wait可以單幹了?
翻箱倒櫃一通倒騰,終於在openJDK源碼里找到了原因:
static void ensure_join(JavaThread* thread) { // We do not need to grab 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(); }
調用鏈是:run() -> thread_main_inner() -> exit() -> ensure_join()。意思就是線程要結束之前肯定會調上邊這個ensure_join方法,而這個方法執行了lock.notify_all(thread)。可見老師沒騙我,wait是需要notify或notifyAll喚醒的,只不過是線程結束時,虛擬機幫我們做了一次notifyAll。
最後奉勸各位小夥伴:
盡量不要使用線程本身的監視器鎖,不然可能會出現非預期的線程喚醒= =、