另一個棘手的核心 Java 問題,wait 和 notify。它們是在有 synchronized 標記的方法或 synchronized 塊中調用的,因為 wait 和 modify 需要監視對其上調用 wait 或 notify-get 的 Object。大多數Java開發人員都知道對象類的 wait(),notify() 和 notifyAll()方法必須在Java中的 synchronized 方法或 synchronized 塊中調用, 但是我們想過多少次, 為什么在 Java 中 wait, notify 和 notifyAll 來自 synchronized 塊或方法?最近這個問題在Java面試中被問到我的一位朋友,他思索了一下,並回答說: 如果我們不從同步上下文中調用 wait() 或 notify() 方法,我們將在 Java 中收到 IllegalMonitorStateException。他的回答從實際效果上年是正確的,但面試官對這樣的答案不會完全滿意,並希望向他解釋這個問題。面試結束后 他和我討論了同樣的問題,我認為他應該告訴面試官關於 Java 中 wait()和 notify()之間的競態條件,如果我們不在同步方法或塊中調用它們就可能存在。讓我們看看競態條件如何在Java程序中發生。它也是流行的線程面試問題之一,並經常在電話和面對面的Java開發人員面試中出現。因此,如果你正在准備Java面試,那么你應該准備這樣的問題,並且可以真正幫助你的一本書是《Java程序員面試公式書》的。這是一本罕見的書,涵蓋了Java訪談的幾乎所有重要主題,例如核心Java,多線程,IO 和 NIO 以及 Spring 和 Hibernate 等框架。你可以在這里查看。為什么要等待來自 Java中的 synchronized 方法的 wait方法為什么必須從 Java 中的 synchronized 塊或方法調用 ?我們主要使用 wait(),notify() 或 notifyAll() 方法用於 Java 中的線程間通信。一個線程在檢查條件后正在等待,例如,在經典的生產者 - 消費者問題中,如果緩沖區已滿,則生產者線程等待,並且消費者線程通過使用元素在緩沖區中創建空間后通知生產者線程。調用notify()或notifyAll()方法向單個或多個線程發出一個條件已更改的通知,並且一旦通知線程離開 synchronized 塊,正在等待的所有線程開始獲取正在等待的對象鎖定,幸運的線程在重新獲取鎖之后從 wait() 方法返回並繼續進行。讓我們將整個操作分成幾步,以查看Java中wait()和notify()方法之間的競爭條件的可能性,我們將使用Produce Consumer 線程示例更好地理解方案:
- Producer 線程測試條件(緩沖區是是否完整)並確認必須等待(找到緩沖區已滿)。
- Consumer 線程在使用緩沖區中的元素后設置條件。
- Consumer 線程調用 notify() 方法; 這是不會被聽到的,因為 Producer 線程還沒有等待。
- Producer 線程調用 wait() 方法並進入等待狀態。
因此,由於競態條件,我們可能會丟失通知,如果我們使用緩沖區或只使用一個元素,生產線程將永遠等待,你的程序將掛起。“在java同步中等待 notify 和 notifyall 現在讓我們考慮如何解決這個潛在的競態條件?這個競態條件通過使用 Java 提供的 synchronized 關鍵字和鎖定來解決。為了調用 wait(),notify() 或 notifyAll(), 在Java中,我們必須獲得對我們調用方法的對象的鎖定。由於 Java 中的 wait() 方法在等待之前釋放鎖定並在從 wait() 返回之前重新獲取鎖定方法,我們必須使用這個鎖來確保檢查條件(緩沖區是否已滿)和設置條件(從緩沖區獲取元素)是原子的,這可以通過在 Java 中使用 synchronized 方法或塊來實現。我不確定這是否是面試官實際期待的,但這個我認為至少有意義,請糾正我如果我錯了,請告訴我們是否還有其他令人信服的理由調用 wait(),notify() 或 Java 中的 notifyAll() 方法。總結一下,我們用 Java 中的 synchronized 方法或 synchronized 塊調用 Java 中的 wait(),notify() 或 notifyAll() 方法來避免:1) Java 會拋出 IllegalMonitorStateException,如果我們不調用來自同步上下文的wait(),notify()或者notifyAll()方法。2) Javac 中 wait 和 notify 方法之間的任何潛在競爭條件。