संभावित नुकसान क्या है अगर यह wait()
एक सिंक्रनाइज़ ब्लॉक के बाहर आह्वान करना संभव था , यह शब्दार्थ को बनाए रखना है - कॉलर थ्रेड को निलंबित करना?
आइए स्पष्ट करें कि हम wait()
एक ठोस उदाहरण के साथ सिंक्रनाइज़ किए गए ब्लॉक के बाहर बुलाया जा सकता है तो हम किन मुद्दों पर चलेंगे ।
मान लीजिए हम एक अवरुद्ध कतार को लागू करने वाले थे (मुझे पता है, एपीआई में पहले से ही एक है :)
पहला प्रयास (बिना सिंक्रोनाइज़ेशन) नीचे दी गई लाइनों के साथ कुछ दिख सकता है
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
यह संभवतः वही हो सकता है:
एक उपभोक्ता धागा कॉल take()
और कहा कि देखता है buffer.isEmpty()
।
इससे पहले कि उपभोक्ता धागा कॉल करने के लिए जाता है wait()
, एक निर्माता धागा साथ आता है और एक पूर्ण आह्वान करता give()
है,buffer.add(data); notify();
उपभोक्ता थ्रेड अब कॉल करेगा wait()
(और याद किया जाएगा notify()
कि बस बुलाया गया था)।
यदि अशुभ है, तो उत्पादक धागा give()
इस तथ्य के परिणामस्वरूप अधिक उत्पादन नहीं करेगा कि उपभोक्ता धागा कभी नहीं उठता है, और हमारे पास एक डेड-लॉक है।
एक बार जब आप इस मुद्दे को समझ लेते हैं, तो समाधान स्पष्ट होता है: synchronized
यह सुनिश्चित करने के लिए उपयोग करें notify
कि कभी भी isEmpty
और के बीच में नहीं बुलाया जाता है wait
।
विवरण में जाने के बिना: यह सिंक्रनाइज़ेशन समस्या सार्वभौमिक है। जैसा कि माइकल बोर्गवर्ड बताते हैं, थ्रेड्स के बीच संचार के बारे में प्रतीक्षा / सूचित सब कुछ है, इसलिए आप हमेशा ऊपर वर्णित एक के समान एक दौड़ की स्थिति के साथ समाप्त करेंगे। यही कारण है कि "केवल सिंक्रनाइज़ के अंदर प्रतीक्षा करें" नियम लागू किया गया है।
@Willie द्वारा पोस्ट किए गए लिंक का एक पैराग्राफ इसे अच्छी तरह से सारांशित करता है:
आपको एक पूर्ण गारंटी की आवश्यकता है कि वेटर और नोटिफ़ायर विधेय की स्थिति के बारे में सहमत हैं। वेटर किसी बिंदु पर विधेय की स्थिति की जाँच करता है थोड़ा सोने से पहले, लेकिन यह विधेय पर सही होने के लिए निर्भर करता है जब वह सोने जाता है। उन दो घटनाओं के बीच भेद्यता की अवधि है, जो कार्यक्रम को तोड़ सकती है।
निर्माता और उपभोक्ता को इस बात पर सहमत होने की आवश्यकता है कि उपर्युक्त उदाहरण में है buffer.isEmpty()
। और यह सुनिश्चित करने के लिए समझौता किया जाता है कि synchronized
ब्लॉक में प्रतीक्षा और सूचना का प्रदर्शन किया जाए ।
इस पोस्ट को यहां एक लेख के रूप में फिर से लिखा गया है: जावा: प्रतीक्षा को एक सिंक्रनाइज़ ब्लॉक में क्यों बुलाया जाना चाहिए