2008-10-22 10 views
33

किसी ने काम पर सिर्फ एक सिंक्रनाइज़ के अंदर प्रतीक्षा को लपेटने के पीछे तर्क के लिए कहा।क्या कोई थ्रेड मॉनीटर समझा सकता है और इंतजार कर सकता है?

ईमानदारी से मैं तर्क नहीं देख सकता। मैं समझता हूं कि javadocs क्या कहता है - कि धागे को ऑब्जेक्ट के मॉनीटर का मालिक होना चाहिए, लेकिन क्यों? इससे क्या समस्याएं रुकती हैं? (और अगर यह वास्तव में आवश्यक है, क्यों नहीं इंतजार विधि की निगरानी में ही मिल सकता है?)

मैं एक काफी गहराई क्यों या शायद एक एक लेख के संदर्भ के लिए देख रहा हूँ। मैं एक त्वरित गूगल में नहीं मिला।

ओह, भी, कैसे तुलना Thread.Sleep करता है?

संपादित करें: उत्तर का महान सेट - मैं वास्तव में चाहता हूं कि मैं एक से अधिक का चयन कर सकूं क्योंकि उन सभी ने मुझे यह समझने में मदद की कि क्या हो रहा था।

उत्तर

3

वस्तु वस्तु पर नजर रखने के मालिक नहीं है, तो जब यह Object.wait() कहता है, जब तक की निगरानी जारी किया गया है एक श्रोता को सूचित सेटअप करने के लिए वस्तु का उपयोग करने के लिए सक्षम नहीं होंगे। इसके बजाय, इसे सिंक्रनाइज़ ऑब्जेक्ट पर किसी विधि तक पहुंचने का प्रयास करने वाले थ्रेड के रूप में माना जाएगा।

public void doStuffOnThisObject() 

और निम्न विधि:

या दूसरे शब्दों में कहें लिए, वहाँ कोई अंतर नहीं है

public void wait() 

दोनों ही तरीकों से अवरुद्ध हो जाएगा जब तक वस्तु की निगरानी जारी है। किसी ऑब्जेक्ट की स्थिति को एक से अधिक थ्रेड द्वारा अपडेट होने से रोकने के लिए यह जावा में एक विशेषता है। यह प्रतीक्षा() विधि पर बस अनपेक्षित परिणाम है।

संभवतः, प्रतीक्षा() विधि सिंक्रनाइज़ नहीं होती है क्योंकि इससे ऐसी स्थितियां पैदा हो सकती हैं जहां थ्रेड के ऑब्जेक्ट पर एकाधिक ताले होते हैं। (इस पर अधिक जानकारी के लिए Java Language Specifications/Locking देखें।) एकाधिक ताले एक समस्या है क्योंकि प्रतीक्षा() विधि केवल एक लॉक को पूर्ववत कर देगी। यदि विधि सिंक्रनाइज़ की गई थी, तो यह गारंटी होगी कि संभावित बाहरी लॉक पूर्ववत होने पर केवल विधि का लॉक पूर्ववत हो जाएगा। यह कोड में एक डेडलॉक स्थिति पैदा करेगा।

Thread.Sleep पर आपके प्रश्न का उत्तर देने के लिए(), Thread.Sleep() कि जो कुछ भी हालत आप पर इंतजार कर रहे हैं मिला दिया गया है गारंटी नहीं है। Object.wait() और Object.notify() का उपयोग करना प्रोग्रामर को मैन्युअल रूप से अवरुद्ध करने की अनुमति देता है। अधिसूचना भेजी जाने के बाद थ्रेड अनब्लॉक हो जाएंगे कि एक शर्त पूरी हो गई है। जैसे डिस्क से पढ़ा गया है और धागे द्वारा डेटा संसाधित किया जा सकता है। Thread.sleep() को अगर स्थिति पूरी हो गई है तो प्रोग्रामर को मतदान करने की आवश्यकता होगी, फिर यदि सो नहीं है तो सो जाओ।

+0

मैं सिंक्रनाइज़ समझता हूं। सवाल यह था कि ऑब्जेक्ट.वाइट() को मॉनीटर का स्वामित्व क्यों होना चाहिए, या ऑब्जेक्ट के अंदर उस मॉनिटर का अधिग्रहण क्यों नहीं किया जा सकता है।प्रतीक्षा करें() विधि (या यह है कि Thread.sleep() क्या है? –

+0

क्षमा करें, मुझे गलत समझा गया है। उत्तर अपडेट किया गया है। क्या यह आपके प्रश्न का बेहतर उत्तर देता है? – 64BitBob

+1

हां, यह एक अच्छा सौदा करता है, हालांकि खुला प्रश्न है अभी भी "प्रतीक्षा विधि क्यों सिंक्रनाइज़ नहीं हो सकती है?" –

4

इसे मॉनिटर के मालिक होने की आवश्यकता है, क्योंकि प्रतीक्षा() का उद्देश्य मॉनीटर को छोड़ना है और अन्य धागे मॉनिटर को स्वयं प्रसंस्करण करने के लिए प्राप्त करते हैं। इन तरीकों का उद्देश्य (प्रतीक्षा/सूचित करें) दो धागे के बीच सिंक्रनाइज़ कोड ब्लॉक तक पहुंच समन्वयित करना है जिसके लिए एक-दूसरे को कुछ कार्यक्षमता करने की आवश्यकता होती है। यह सुनिश्चित करने की बात नहीं है कि डेटा संरचना तक पहुंच थ्रेडसेफ है, लेकिन एकाधिक धागे के बीच घटनाओं को समन्वयित करने के लिए।

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

+0

अच्छा बिंदु, लेकिन उस मामले के बारे में जहां आप बस किसी अन्य थ्रेड से कुछ अधिसूचना पर इंतजार करना चाहते हैं, मॉनीटर को रिलीज़ न करें ताकि एक और धागा हो। आप कहते हैं "थ्रेआ d.sleep "इस उपयोग के लिए अधिक उपयुक्त है? –

+0

उस स्थिति में, आपको शायद उन दो धागे के बीच सिंक्रनाइज़ करने के लिए एक और मॉनीटर बनाना चाहिए। –

+0

यदि डेटा संरचनाओं की आवश्यकता नहीं है, लेकिन आप बस एक घटना की प्रतीक्षा कर रहे हैं, तो नींद पर्याप्त हो सकती है, लेकिन घटना धागे को सोने के थ्रेड को जागने के लिए बाधा डालना होगा (जब तक कि समय न हो)। जिसका अर्थ है कि इसे थ्रेड ऑब्जेक्ट तक पहुंच की आवश्यकता होगी। – Robin

5

प्रतीक्षा करें मॉनीटर को छोड़ देता है, इसलिए आपको इसे देने के लिए होना चाहिए। अधिसूचना भी मॉनिटर होना चाहिए।

मुख्य कारण यह है कि आप ऐसा करना चाहते हैं यह सुनिश्चित करने के लिए कि जब आप प्रतीक्षा() से वापस आते हैं तो मॉनीटर होता है - आमतौर पर, आप कुछ साझा संसाधनों की सुरक्षा के लिए प्रतीक्षा/अधिसूचना प्रोटोकॉल का उपयोग कर रहे हैं और आप इसे चाहते हैं रिटर्न का इंतजार करते समय इसे छूने के लिए सुरक्षित रहें। सूचित करने के साथ ही - आमतौर पर आप कुछ बदल रहे हैं और फिर अधिसूचना() को कॉल करना चाहते हैं - आप मॉनिटर रखना चाहते हैं, बदलाव करना चाहते हैं, और कॉल को सूचित करना चाहते हैं()।

आप इस तरह एक समारोह बना दिया है:

public void synchWait() { 
    syncronized { wait(); } 
} 

आप जब इंतजार लौटे मॉनिटर नहीं होगा - आप इसे प्राप्त कर सकते हैं, लेकिन आप इसे अगले नहीं मिल सकता है।

2

यहां मेरी समझ है कि प्रतिबंध वास्तव में एक आवश्यकता क्यों है। मैं इसे एक सी ++ मॉनीटर कार्यान्वयन पर आधारित कर रहा हूं जिसे मैंने एक म्यूटेक्स और एक कंडीशन वैरिएबल के संयोजन से थोड़ी देर पहले बनाया था।

एक म्युटेक्स + condition_variable = मॉनिटर प्रणाली में, wait कॉल प्रतीक्षा राज्य और विज्ञप्ति म्युटेक्स में हालत चर सेट। कंडीशन वैरिएबल साझा राज्य है, इसलिए इसे प्रतीक्षा करने के लिए इच्छित धागे और थ्रेड के बीच दौड़ की स्थिति से बचने के लिए इसे लॉक करने की आवश्यकता है। अपने राज्य को लॉक करने के लिए अभी तक एक और म्यूटेक्स पेश करने के बजाय, मौजूदा म्यूटेक्स का उपयोग किया जाता है। जावा में, म्यूटेक्स सही ढंग से लॉक होता है जब लगभग-प्रतीक्षा-प्रतीक्षा थ्रेड मॉनीटर का मालिक होता है।

13

पहले से ही बहुत अच्छे उत्तर यहां दिए गए हैं। लेकिन बस यहां उल्लेख करना चाहते हैं कि प्रतीक्षा करें() का उपयोग करते समय अन्य को करना चाहिए उस स्थिति पर निर्भर एक लूप में करना जो आप प्रतीक्षा कर रहे हैं यदि आप नकली जागरूकता देख रहे हैं, जो मेरे अनुभव में होता है।

कुछ अन्य धागे के लिए प्रतीक्षा करने के लिए सही करने के लिए एक शर्त बदल सकते हैं और सूचित करने के लिए:

synchronized(o) { 
    while(! checkCondition()) { 
    o.wait(); 
    } 
} 
बेशक

, इन दिनों, मैं सिर्फ नए स्थिति वस्तु का उपयोग कर के रूप में यह स्पष्ट है और अधिक सुविधाएं हैं की सलाह देते हैं (प्रति लॉक की कई स्थितियों की अनुमति देने की तरह, प्रतीक्षा कतार की लंबाई, अधिक लचीला अनुसूची/बाधा, आदि की जांच करने में सक्षम होना)।

Lock lock = new ReentrantLock(); 
Condition condition = lock.newCondition(); 
lock.lock(); 
try { 
    while (! checkCondition()) { 
    condition.await(); 
    } 
} finally { 
    lock.unlock(); 
} 

} अगर वहाँ एक शर्त है

2

अधिकतर इंतजार किया जाता है का कहना है कि एक कतार खाली है।

If(queue is empty) 
    queue.wait(); 

मान लें कि कतार खाली है। यदि वर्तमान थ्रेड कतार की जांच के बाद पूर्व-empts, यदि कोई अन्य थ्रेड कतार में कुछ तत्व जोड़ता है, तो वर्तमान थ्रेड नहीं पता होगा और प्रतीक्षा स्थिति के लिए जाएगा। यह गलत है। तो हम अब हम क्या करने पर विचार अगर वे खुद के रूप में सिंक्रनाइज़ इंतजार किए जाने

Synchornized(queue) 
{ 
    if(queue is empty) 
      queue.wait(); 
} 

की तरह कुछ होना चाहिए। जैसा कि पहले से ही एक टिप्पणी में उल्लेख किया गया है, यह केवल एक ताला जारी करता है। इसका मतलब है कि यदि उपरोक्त कोड में प्रतीक्षा() सिंक्रनाइज़ किया गया था तो केवल एक लॉक जारी किया गया होगा। तात्पर्य है कि वर्तमान धागा कतार के लिए ताला के साथ प्रतीक्षा के लिए जायेगा।

संबंधित मुद्दे