2012-05-21 12 views
9

कल्पना कीजिए कि आपके पास जावा में एक ठेठ निर्माता-उपभोक्ता पैटर्न है। थोड़ा अधिक कुशल होने के लिए आप notify() और notifyAll() का उपयोग नहीं करना चाहते हैं जब कतार में कोई नया तत्व जोड़ा जाता है। यदि दो निर्माता धागे सूचित करते हैं, तो क्या यह गारंटी है कि दो अलग प्रतीक्षा उपभोक्ता धागे जाग जाएंगे? या यह हो सकता है कि दो notify() एस एक दूसरे के तुरंत बाद निकाल दिया जाए, वही कॉमसमर थ्रेड को दो बार जागने के लिए कतारबद्ध किया जाए? मुझे यह नहीं मिल रहा है कि यह अनुभाग एपीआई है जो वर्णन करता है कि यह वास्तव में कैसे काम करता है। क्या जावा में एक बार धागे को जागने के लिए कुछ परमाणु आंतरिक ऑपरेशन होता है?एक ही धागे को कई बार जागृत करने के बारे में सूचित कर सकते हैं?

यदि केवल एक कॉमसमर इंतजार कर रहा है तो दूसरा अधिसूचना खो जाएगी, यह कोई समस्या नहीं है।

+4

आप केवल ब्लॉकिंगक्यूयू कार्यान्वयन का उपयोग क्यों नहीं करते? –

+0

मैं मॉरीसिओ से सहमत हूं। प्रतीक्षा करें()/सूचित करें() यदि संभव हो तो साफ़ करें - यह रेसिंग की स्थिति का एक हॉर्नेट घोंसला है, खराब दस्तावेज एपीआई और "नकली wakeups"। –

उत्तर

13

मेरे उत्तर में कुछ कार्यान्वयन विशिष्ट जानकारी है। यह सूर्य जेवीएम और अन्य थ्रेड लाइब्रेरी व्यवहार के मेरे कामकाजी ज्ञान पर आधारित है।

यदि दो निर्माता धागे सूचित करते हैं, तो क्या यह गारंटी है कि दो अलग प्रतीक्षा उपभोक्ता धागे जाग जाएंगे?

नहीं, यह नहीं है। इस बात की कोई गारंटी नहीं है कि कोई भी उपभोक्ता जाग जाएगा। गारंटी क्या है कि यदि 2 धागे इंतजार कर रहे हैं, तो 2 अलग धागे को रन कतार में रखा जाएगा।

या यह हो सकता है कि दो notify() रों एक दूसरे के कारण ही उपभोक्ताओं धागा दो बार Wakeup के लिए पंक्तिबद्ध किए जाने के बाद शीघ्र ही निकाल दिया?

संख्या दो notify() कॉल एक ही उपभोक्ता धागे को दो बार कतारबद्ध नहीं करेगा। हालांकि, यह एक थ्रेड को जागृत करने का कारण बन सकता है और अन्य धागे इंतजार नहीं कर सकते हैं, इसलिए दूसरा notify() कॉल कुछ भी नहीं कर सकता है। बेशक धागा जागृत हो सकता था और फिर फिर से इंतजार कर रहा था और फिर दूसरी notify() उस तरह से कॉल करें, लेकिन मुझे नहीं लगता कि आप यही पूछ रहे हैं।

क्या जावा में एक बार थ्रेड को जागने के लिए कुछ परमाणु आंतरिक ऑपरेशन होता है?

हां। Thread कोड में कई सिंक्रनाइज़ेशन अंक हैं। एक बार थ्रेड अधिसूचित होने के बाद इसे wait कतार से बाहर ले जाया जाता है। notify() पर भविष्य की कॉल wait कतार में दिखाई देगी और धागे को नहीं मिलेगी।

एक और महत्वपूर्ण बिंदु। निर्माता/उपभोक्ता मॉडल के साथ, हमेशा सुनिश्चित करें कि आप while लूप में स्थिति का परीक्षण कर रहे हैं। इसका कारण यह है कि उपभोक्ताओं के साथ दौड़ की स्थिति है जो ताला पर अवरुद्ध हैं लेकिन इस स्थिति पर इंतजार नहीं कर रहे हैं।

synchronized (workQueue) { 
    // you must do a while here 
    while (workQueue.isEmpty()) { 
     workQueue.wait(); 
    } 
    workQueue.remove(); 
} 

Consumer1workQueue पर इंतजार किया जा सकता है। Consumer2synchronized पर लेकिन रन-कतार में अवरुद्ध किया जा सकता है। अगर workQueue और workQueue.notify() में कुछ डाल दिया जाता है। Consumer2 अब रन-कतार में रखा गया है लेकिन Consumer1 के पीछे है जो पहले वहां है। यह एक आम कार्यान्वयन है। तो Consumer1workQueue से आइटम को हटा देता है जो Consumer2 के बारे में अधिसूचित किया गया था। Consumer2 को फिर से परीक्षण करना है यदि workQueue खाली है अन्यथा remove() फेंक देगा क्योंकि कतार फिर से खाली है। more details of the race के लिए यहां देखें।

यह जानना भी महत्वपूर्ण है कि नकली जागरूकता दस्तावेज की गई है ताकि while लूप wait() कॉल के बिना जागने वाले थ्रेड के खिलाफ सुरक्षा करता है।

यह सब कहा गया है, अगर आप BlockingQueue का उपयोग कर अपने निर्माता/उपभोक्ता कोड को अन्य उत्तरों में अनुशंसित कर सकते हैं तो आपको ऐसा करना चाहिए। BlockingQueue कोड पहले से ही इन सभी समस्याओं का समाधान कर चुका है।

+0

सही उत्तर, मेरा प्रश्न थोड़ा अस्पष्ट होना चाहिए, लेकिन आप समझ गए कि मैं क्या सुनना चाहता था :-) –

2
से

javadoc for notify():

विकल्प जो धागे की सूचना देने के लिए "मनमाना है और कार्यान्वयन के विवेक पर होता है"

यह लगभग निश्चित रूप नहीं होगा एक 'निष्पक्ष' (के रूप में शब्द है कंप्यूटर विज्ञान और समांतरता में प्रयोग किया जाता है) थ्रेड जागने के लिए एल्गोरिदम। यह पूरी तरह से संभव है कि एक ही धागा जल्दी उत्तराधिकार में दो बार जाग गया है। यह भी ध्यान रखें कि नकली नोटिफिकेशन भी संभव है।

सामान्यतः, मैं उन टिप्पणियों से सहमत हूं जो BlockingQueue कार्यान्वयन का उपयोग करने का सुझाव देते हैं।

3

हां, आप जो वर्णन कर सकते हैं वह हो सकता है।

जैसा कि javadoc में वर्णित है, notify एक मनमाना धागा जगाता है। तो यदि आपका धागा समाप्त हो गया है और अगले notify से पहले wait कहलाता है, तो यह जागने के लिए मनमाना उम्मीदवारों में से एक है।

मैं बहु अनुप्रयोगों के साथ व्यापक अनुभव है, और मुझे लगता है कि मैं इन दो पैटर्न का उपयोग करें:

  1. जिसमें एकाधिक सो सूत्र जो एक घटना पर जाग करने की जरूरत हैं, और वे जागते हैं इससे कोई फर्क नहीं पड़ता। इस मामले में, मैं उन्हें जागने के लिए notifyAll का उपयोग करता हूं।

  2. एक नींद धागा है जिसे किसी घटना पर जागने की आवश्यकता होती है। इस मामले में, मैं इसे जागने के लिए notify का उपयोग करता हूं।

मैंने कभी एक स्थिति है जहाँ कई सो धागे देखते हैं है और मैं उनमें से केवल एक को जगाने के लिए चाहते हैं, मैं एक अलग डिजाइन का उपयोग इस लक्ष्य को हासिल करने के लिए। असल में, मैं खुद को कुछ बनाता हूं ताकि रनटाइम पर्यावरण द्वारा कोई मनमाने ढंग से निर्णय नहीं लिया जा सके। मैं हमेशा जानना चाहता हूं कि क्या होगा।

मैंने इन दो परिदृश्यों में से एक में एक डिज़ाइन तोड़ दिया, या मैं java.util.concurrent पैकेज से कुछ उपयोग करता हूं। मुझे नकली अधिसूचनाओं में कभी समस्या नहीं है, लेकिन मैं लॉकिंग के लिए उपयोग की जाने वाली वस्तुओं पर भी बहुत सावधान हूं। मैं वेनिला Object उदाहरण बनाते हैं जिनके एकमात्र उद्देश्य लॉक ऑपरेशन का लक्ष्य होना है, लेकिन कभी-कभी मैं उस ऑब्जेक्ट का उपयोग करूंगा जिसका वर्ग प्रकार अच्छी तरह परिभाषित है और मेरे नियंत्रण में है।

2

आप उचित आगमन-आदेश नीति प्राप्त करने के लिए ReentrantLock का उपयोग कर सकते हैं। इंटरफ़ेस ReadWriteLock, आप निर्माता-उपभोक्ता व्यवहार प्राप्त करते हैं। कक्षा ReentrantReadWriteLock दोनों क्षमताओं को गठबंधन करें।

या

आप ArrayBlockingQueue, जहां इस पद्धति पहले से ही लागू किया गया है का उपयोग करना चाहिए।

int capacity = 10; 
boolean fair = true; 
new ArrayBlockingQueue(capacity, fair); 
संबंधित मुद्दे