2009-06-29 17 views
13

मेरे पास एक बुनियादी सवाल है। क्यों और कैसे चयन करने योग्य चैनल की रजिस्टर विधि कॉल अवरुद्ध करने में हो सकती है। मुझे एक परिदृश्य प्रदान करने दें।जावा थ्रेड ब्लॉक। क्या करें?

मैंने निम्नानुसार श्रेणी रजिस्टर में एक चयनकर्ता वस्तु बनाई है।

private static Selector selector = Selector.open(); 

मेरे पास चयनकर्ता के साथ चैनल पंजीकृत करने के लिए एक ही कक्षा (रजिस्टर) में एक विधि भी है।

public static SelectionKey registerChannel(SelectableChannel channel, 
      int ops) throws IOException { 

     channel.configureBlocking(false); 
     return channel.register(selector, ops); 
    } 

और वहाँ एक और वर्ग का अनुरोध नाम है, जो विधि है जो चैनलों, प्रक्रियाओं और कॉल चैनल रजिस्टर करने के लिए विधि का अनुसरण करने से डेटा पढ़ता है।

selectonKey = Register.register(socketChannel, SelectionKey.OP_READ); 

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

किसी भी इनपुट की सराहना की जाएगी।

जो मैंने वर्णित किया है उसे जोड़ना। आगे के परीक्षणों से पता चला कि यदि रजिस्टर.रेस्टर विधि को उसी धागे से बुलाया जाता है, तो यह पंजीकरण करने में सक्षम होता है लेकिन उसके बाद यदि कोई अन्य थ्रेड विधि को आमंत्रित करने का प्रयास करता है, तो थ्रेड आगे नहीं बढ़ता है।

उत्तर

0

क्या आपने यूनिक्स में kill -QUIT या विंडोज़ में Ctrl + Break या jstack उपयोगिता का उपयोग करके अपने प्रोग्राम में सभी थ्रेडों का एक स्टैक ट्रेस प्रिंट करने का प्रयास किया है?

AbstractSelectableChannel में एक ताला है जिसमें configureBlocking और register सिंक्रनाइज़ करने की आवश्यकता है। यह लॉक भी blockingLock() विधि के माध्यम से सुलभ है, और इसलिए एक और थ्रेड संभावित रूप से लॉक धारण कर सकता है जिससे आपका रजिस्टर कॉल अनिश्चित काल तक अवरुद्ध हो सकता है (लेकिन बिना किसी स्टैक ट्रेस के यह कहना मुश्किल है)।

7

आपको लॉक का उपयोग करने और मैन्युअल रूप से सिंक्रनाइज़ करने की आवश्यकता है।

एक ही धागे में आप चयनकर्ता पाश द्वारा चलाए जा रहे एक ReentrantLock है:

final ReentrantLock selectorLock = new ReentrantLock(); 

फिर जब आप चयनकर्ता के साथ रजिस्टर करने के लिए करते हैं की जरूरत है कुछ इस तरह:

selectorLock.lock(); 
try { 
    selector.wakeup(); 
    socketChannel.register(selector, ops); 
} finally { 
    selectorLock.unlock(); 
} 

अंत में, के दौरान अपने लूप जिसे आप स्वीकार कर रहे हैं(), इस तरह कुछ:

selectorLock.lock(); 
selectorLock.unlock(); 

selector.select(500); 

और टी मुर्गी आपके बाकी तर्क के साथ जारी है।

इस निर्माण की गारंटी देता है कि register() कॉल इसी wakeup() और register() कॉल के बीच कभी भी किसी अन्य select() है कि वहाँ सुनिश्चित करने के द्वारा बंद नहीं होंगे।

+0

कोड नमूने के लिए +1। मैं अनुभव से यह दूसरा। अच्छी तरह से किया। – casey

+0

यह प्रत्येक पंजीकरण के लिए 500 एमएस अनावश्यक विलंबता को जोड़ता है। इसके बजाय http://stackoverflow.com/a/2179612/448970 में दृष्टिकोण का उपयोग करें। –

+0

@ डेविड बी। यह प्रत्येक पंजीकरण में कोई विलंबता नहीं जोड़ता है। यह चुनिंदा धागा है जो 500ms तक ब्लॉक करता है, रजिस्टर थ्रेड नहीं, और चयन थ्रेड हमेशा 'select()' में अवरुद्ध करना चाहता है। उत्तर में दावों में आप अधिकतर गलत हैं। – EJP

28

यह अधिकांश एनआईओ कार्यान्वयन की मूलभूत विशेषता है जो दस्तावेज़ीकरण से स्पष्ट नहीं है।

आपको अपने चयन या डेडलॉक्स करने वाले एक ही थ्रेड से सभी रजिस्टर कॉल करने की आवश्यकता है। आम तौर पर यह रजिस्ट्रेशन/अपंजीकरण/ब्याज-परिवर्तनों की कतार प्रदान करके किया जाता है जो कि लिखा जाता है और फिर selector.wakeup() कहा जाता है। जब चयन धागा जागता है तो यह कतार की जांच करता है और किसी भी अनुरोधित संचालन करता है।

+1

यह * सभी * एनआईओ कार्यान्वयन की मूलभूत विशेषता है जो पूरी तरह से जावाडोक में निर्दिष्ट है। जब आप 'select(),' और 'रजिस्टर()' में से एक होते हैं, तो उनमें से तीन प्रयासों में तीन नेस्टेड सिंक्रनाइज़ेशन होते हैं। परिणाम एक डेडलॉक नहीं है लेकिन एक * ब्लॉक, * जो समवर्ती 'चयन()' कॉल रिटर्न तक ही रहता है। समाधान 'रजिस्टर()' से पहले 'wakeup()' को कॉल करना है। '' 'चुनने के लिए' चयन() 'को अनब्लॉक करने और शून्य लौटने के लिए मजबूर करता है, जो इसके तीन ताले जारी करता है, जो 'रजिस्टर()' को उस व्यक्ति का दावा करने की अनुमति देता है जिसकी आवश्यकता है और आगे बढ़ो। – EJP

+2

उपरोक्त टिप्पणी में प्रदान किया गया समाधान गलत है - कम से कम, स्पष्टीकरण दिया गया है। धागे के शेड्यूलिंग के आधार पर, चयन थ्रेड पूरे लूप को पूरा कर सकता है और रजिस्टर थ्रेड रजिस्टर() को कॉल करने में सक्षम होने से पहले चयन() को फिर से चालू कर सकता है। – dsd

0

किसी भी धागे से अपने चैनल रजिस्टर:

synchronized (selectorLock2) { 
    selector.wakeup(); 
    synchronized (selectorLock1) { 
     channel.register(selector, ops); 
    } 
} 

आपका चयनकर्ता पाश इस तरह दिखना चाहिए:

while (true) { 
    synchronized (selectorLock1) { 
     selector.select(); 
    } 
    synchronized (selectorLock2) {} 

    .... 
} 
+0

आपको केवल एक लॉक की आवश्यकता है जैसा कि https://stackoverflow.com/a/1112809/194894 में दिखाया गया है – Flow

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