首先我們要知道Java中的每個類的父類都是Object,而Object中對象頭中有個位置用來儲存鎖資訊。這塊将在進階部分進行深入講解。
為什麼wait方法必須在synchronized保護的同步代碼中使用?
此方法會導緻目前線程将自己放入該對象的等待集中,然後放棄對此對象的所有同步聲明。線程T出于線程排程目的而被禁用,并處于休眠狀态。其實就是讓目前線程運作該該對象處,并讓出CPU。
先來段源碼,因為源碼說要“該方法的調用必須是擁有對象的鎖"。也就是通過synchronized方法或代碼塊擷取對象的鎖。但是為啥要這麼設計呢?其實要從該方法的作用說起,該方法是讓CPU讓出,停在該對象處的等待隊列中。如果不加鎖,其他線程就可以随意進入該對象并修改該對象的狀态。
* <p>
* A thread can also wake up without being notified, interrupted, or
* timing out, a so-called <i>spurious wakeup</i>. While this will rarely
* occur in practice, applications must guard against it by testing for
* the condition that should have caused the thread to be awakened, and
* continuing to wait if the condition is not satisfied. In other words,
* waits should always occur in loops, like this one:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait(timeout);
* ... // Perform action appropriate to condition
* }
* </pre>
* * This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
這裡還存在一個“虛假喚醒”(spurious wakeup)的問題,線程可能在既沒有被notify/notifyAll,也沒有被中斷或者逾時的情況下被喚醒,這種喚醒是我們不希望看到的。雖然在實際生産中,虛假喚醒發生的機率很小,但是程式依然需要保證在發生虛假喚醒的時候的正确性,是以就需要采用while循環的結構。
while (condition does not hold)
obj.wait();
這樣即使被虛假喚醒,也會再次檢查while裡邊的條件,如果不滿足條件,就會繼續wait,也就消除了虛假喚醒的風險。
為什麼wait/notify/notifyAll被定義在Object類中,而Sleep定義在Thread類中?
主要有兩點原因:
-
因為 Java 中每個對象都有一把稱之為 monitor 螢幕的鎖,由于每個對象都可以上鎖,這就要求在對象頭中有一個用來儲存鎖資訊的位置。這個鎖是對象級别的,而非線程級别的,wait/notify/notifyAll 也都是鎖級别的操作,它們的鎖屬于對象,是以把它們定義在 Object 類中是最合适,因為 Object 類是所有對象的父類。
因為如果把 wait/notify/notifyAll 方法定義在 Thread 類中,會帶來很大的局限性,比如一個線程可能持有多把鎖,以便實作互相配合的複雜邏輯,假設此時 wait 方法定義在 Thread 類中,如何實作讓一個線程持有多把鎖呢?又如何明确線程等待的是哪把鎖呢?既然我們是讓目前線程去等待某個對象的鎖,自然應該通過操作對象來實作,而不是操作線程。
wait/notify和sleep方法的異同?
主要對比 wait 和 sleep 方法,我們先說相同點:
- 它們都可以讓線程阻塞。
-
它們都可以響應 interrupt 中斷:在等待的過程中如果收到中斷信号,都可以進行響應,并抛出 InterruptedException 異常。
不同點:
- wait 方法必須在 synchronized 保護的代碼中使用,而 sleep 方法并沒有這個要求。
- 在同步代碼中執行 sleep 方法時,并不會釋放 monitor 鎖,但執行 wait 方法時會主動釋放 monitor 鎖。
- sleep 方法中會要求必須定義一個時間,時間到期後會主動恢複,而對于沒有參數的 wait 方法而言,意味着永久等待,直到被中斷或被喚醒才能恢複,它并不會主動恢複。
-
wait/notify 是 Object 類的方法,而 sleep 是 Thread 類的方法。
以上就是關于 wait/notify 與 sleep 的異同點。