जाहिर है, notify
वेट (किसी भी) एक सेट notifyAll
वेट सेट में, सभी थ्रेड्स को वेटिंग सेट में जगाता है। निम्नलिखित चर्चा को किसी भी संदेह को स्पष्ट करना चाहिए। notifyAll
ज्यादातर समय इस्तेमाल किया जाना चाहिए। यदि आप सुनिश्चित नहीं हैं कि notifyAll
किसका उपयोग करना है, तो उपयोग करें। कृपया स्पष्टीकरण देखें जो निम्न प्रकार है।
बहुत ध्यान से पढ़ें और समझें। यदि आपके कोई प्रश्न हैं, तो कृपया मुझे एक ईमेल भेजें।
निर्माता / उपभोक्ता को देखें (धारणा दो तरीकों के साथ एक निर्माता-निर्माता वर्ग है)। IT BROKEN (क्योंकि यह उपयोग करता है notify
) - हाँ यह काम करता है - यहां तक कि ज्यादातर समय, लेकिन यह गतिरोध भी पैदा कर सकता है - हम देखेंगे क्यों:
public synchronized void put(Object o) {
while (buf.size()==MAX_SIZE) {
wait(); // called if the buffer is full (try/catch removed for brevity)
}
buf.add(o);
notify(); // called in case there are any getters or putters waiting
}
public synchronized Object get() {
// Y: this is where C2 tries to acquire the lock (i.e. at the beginning of the method)
while (buf.size()==0) {
wait(); // called if the buffer is empty (try/catch removed for brevity)
// X: this is where C1 tries to re-acquire the lock (see below)
}
Object o = buf.remove(0);
notify(); // called if there are any getters or putters waiting
return o;
}
पहले तो,
हमें प्रतीक्षा के दौरान थोड़ी देर के लूप की आवश्यकता क्यों है?
while
इस स्थिति को प्राप्त करने के लिए हमें एक लूप की आवश्यकता है:
उपभोक्ता 1 (C1) सिंक्रनाइज़ ब्लॉक में प्रवेश करता है और बफर खाली होता है, इसलिए C1 को प्रतीक्षा सेट ( wait
कॉल के माध्यम से ) में रखा जाता है। कंज्यूमर 2 (C2) सिंक्रोनाइज़ किए गए तरीके (बिंदु Y ऊपर) दर्ज करने वाला है, लेकिन निर्माता P1 बफर में एक ऑब्जेक्ट डालता है, और बाद में कॉल करता है notify
। एकमात्र प्रतीक्षा धागा C1 है, इसलिए इसे जगाया जाता है और अब बिंदु X (ऊपर) पर ऑब्जेक्ट लॉक को फिर से प्राप्त करने का प्रयास करता है।
अब C1 और C2 सिंक्रोनाइज़ेशन लॉक का अधिग्रहण करने का प्रयास कर रहे हैं। उनमें से एक (nondeterministically) चुना जाता है और विधि में प्रवेश करता है, दूसरे को अवरुद्ध किया जाता है (प्रतीक्षा नहीं - लेकिन अवरुद्ध, विधि पर ताला प्राप्त करने की कोशिश कर रहा है)। मान लीजिए कि C2 को पहले ताला मिला है। C1 अभी भी अवरुद्ध है (X पर लॉक को प्राप्त करने की कोशिश कर रहा है)। C2 विधि को पूरा करता है और लॉक जारी करता है। अब, C1 लॉक का अधिग्रहण करता है। लगता है क्या, भाग्यशाली है कि हमारे पास एक while
लूप है, क्योंकि, C1 लूप चेक (गार्ड) करता है और बफर से एक गैर-मौजूद तत्व को हटाने से रोका जाता है (C2 पहले से ही मिल गया है!)। यदि हमारे पास नहीं था while
, तो हमें IndexArrayOutOfBoundsException
बफर के पहले तत्व को हटाने की कोशिश करने वाला C1 मिलेगा !
अभी,
ठीक है, अब हमें InformAll की आवश्यकता क्यों है?
ऊपर के निर्माता / उपभोक्ता उदाहरण में ऐसा लगता है कि हम दूर हो सकते हैं notify
। यह इस तरह लगता है, क्योंकि हम साबित कर सकते हैं कि निर्माता और उपभोक्ता के लिए प्रतीक्षा छोरों पर गार्ड पारस्परिक रूप से अनन्य हैं। यही है, ऐसा लगता है कि हमारे पास put
विधि के साथ-साथ विधि में प्रतीक्षा करने वाला एक धागा नहीं हो सकता है get
, क्योंकि, इसके लिए सत्य होना है, तो निम्नलिखित को सच होना होगा:
buf.size() == 0 AND buf.size() == MAX_SIZE
(मान लें कि MAX_SIZE 0 नहीं है)
फिर भी, यह काफी अच्छा नहीं है, हम उपयोग करने की आवश्यकता है notifyAll
। आइए देखें क्यों ...
मान लें कि हमारे पास आकार 1 का एक बफर है (उदाहरण का अनुसरण करना आसान बनाने के लिए)। निम्नलिखित कदम हमें गतिरोध की ओर ले जाते हैं। ध्यान दें कि किसी भी समय एक थ्रेड को सूचित किया जाता है, यह गैर-नियतकालिक रूप से जेवीएम द्वारा चुना जा सकता है - जो कि किसी भी प्रतीक्षा थ्रेड को जगाया जा सकता है। यह भी ध्यान दें कि जब कई थ्रेड्स एक विधि में प्रवेश पर रोक रहे हैं (यानी लॉक प्राप्त करने की कोशिश कर रहे हैं), अधिग्रहण का क्रम गैर-निर्धारक हो सकता है। यह भी याद रखें कि एक धागा किसी एक समय में केवल एक विधि में हो सकता है - सिंक्रनाइज़ किए गए तरीके केवल एक धागे को कक्षा में किसी भी (सिंक्रनाइज़) विधियों को निष्पादित (यानी लॉक) रखने की अनुमति देते हैं। यदि घटनाओं का निम्नलिखित क्रम होता है - गतिरोध परिणाम:
STEP 1:
- P1 बफर में 1 char डालता है
चरण 2:
- पी 2 प्रयास put
- चेक लूप लूप - पहले से ही एक चार - प्रतीक्षा करता है
चरण 3:
- पी 3 प्रयास put
- चेक लूप लूप - पहले से ही एक चार - प्रतीक्षा करता है
चरण 4:
- C1 1 वर्ण प्राप्त करने का प्रयास करता है
- C2 get
विधि में प्रवेश पर
1 वर्ण - ब्लॉक प्राप्त करने का प्रयास करता है - C3 1 वर्ण प्राप्त करने का प्रयास करता है - get
विधि में प्रवेश पर ब्लॉक
चरण 5:
- C1 get
विधि को निष्पादित कर रहा है - चार हो जाता है, कॉल करता है notify
, बाहर निकलता है विधि
- notify
P2
से पहले उठता है - B2 , C2 विधि से पहले प्रवेश कर सकता है P2 (लॉक को पुनः प्राप्त करना चाहिए), इसलिए put
पद्धति पर प्रविष्टि पर P2 ब्लॉक
- C2 प्रतीक्षा लूप की जांच करता है, बफर में कोई और अधिक चार्ट नहीं है, इसलिए इंतजार करता है
- C3 C2 के बाद विधि में प्रवेश करता है, लेकिन P2 से पहले, प्रतीक्षा लूप की जांच करता है, बफर में और अधिक वर्ण नहीं है, इसलिए इंतजार करता है
चरण 6:
- अब: पी 3, सी 2 और सी 3 प्रतीक्षा है!
- अंत में पी 2 लॉक को अधिग्रहित करता है, बफर में एक चार डालता है, कॉल सूचित करता है, विधि से बाहर निकलता है
चरण 7:
- पी 2 के नोटिफिकेशन पी 3 (किसी भी धागे को जाग्रत किया जा सकता है याद रखें)
- पी 3 प्रतीक्षा लूप स्थिति की जांच करता है, बफर में पहले से ही एक चर है, इसलिए इंतजार करता है।
- अधिक नोटों का उल्लेख नहीं करने के लिए और तीन साल के लिए हमेशा की तरह तैयार!
समाधान: निर्माता / उपभोक्ता कोड (ऊपर) के notify
साथ बदलें notifyAll
।