2011-12-19 14 views
12

मेरे पास एक थ्रेड है जो समय-समय पर इसकी स्थिति अपडेट करता है और मैं चाहता हूं कि दूसरा थ्रेड पहले थ्रेड के लिए इंतजार कर सके। कुछ इस तरह:जावा में किसी ईवेंट की प्रतीक्षा करना - यह कितना मुश्किल है?

Thread 1: 
    while(true) { 
     ...do something... 
     foo.notifyAll() 
     ...wait for some condition that might never happen... 
     ... 
    } 

Thread 2: 
    ... 
    foo.wait(); 
    ... 

अब इस अच्छा लग रहा है और सभी जब तक थ्रेड 1 के notifyAll() धागा 2 के इंतजार(), से पहले चलाता है जो मामले थ्रेड में जब तक 2 प्रतीक्षा करता थ्रेड 1 अधिसूचित फिर (कभी नहीं हो सकता है) ।

मेरे संभव समाधान:

एक) मैं एक CountDownLatch या एक भविष्य इस्तेमाल कर सकते हैं, लेकिन दोनों समस्या यह है कि वे स्वाभाविक केवल एक बारचलाने की है। यही है, थ्रेड 1 के दौरान लूप में, मुझे प्रत्येक बार प्रतीक्षा करने के लिए एक नया फू बनाने की आवश्यकता होगी और थ्रेड 2 को यह पूछने की आवश्यकता होगी कि किस फू का इंतजार है। के रूप में मुझे डर है कि foo = नए FutureTask() में, क्या होता है जब किसी को, के लिए "किसी कारण" (वर्ष foo के लिए इंतजार कर रहे थे सेट आमंत्रित नहीं किया गया मैं बस

while(true) { 
    foo = new FutureTask(); 
    ... 
    foo.set(...); 
    ...wait for a condition that might never be set... 
    ... 
} 

लिखने के बारे में बुरा विचार है, जैसे एक अपवाद हैंडलिंग में बग)?

ख) या मैं एक सेमाफोर इस्तेमाल कर सकते हैं:

class Event { 
    Semaphore sem; 
    Event() { sem = new Semaphore(1); sem . } 
    void signal() { sem.release(); } 
    void reset() { sem.acquire(1); } 
    void wait() { if (sem.tryAcquire(1)) { sem.release(); } } 
} 

लेकिन मुझे डर है, कुछ रेस स्थिति है कि वहाँ एक से अधिक थ्रेड प्रतीक्षा() इसके लिए ing कर रहे हैं, जबकि एक अन्य एक संकेत() और रीसेट () रों।

प्रश्न:

वहाँ जावा एपीआई कि Windows ईवेंट व्यवहार जैसा दिखता में कुछ भी नहीं है? या, यदि आप विंडोज़ को तुच्छ मानते हैं, तो गोलांग के वेट ग्रुप जैसे कुछ (यानी एक काउंटरडाउनलैच जो countUp() को अनुमति देता है)? कुछ भी?

मैन्युअल रूप से ऐसा करने के लिए:

थ्रेड 2 बस नकली Wakeup की वजह से इंतजार नहीं कर सकता और जावा में कोई रास्ता नहीं है पता करने के लिए क्यों Object.wait() लौट आए है। तो मुझे एक शर्त चर की आवश्यकता है जो स्टोर करता है कि घटना संकेतित है या नहीं। थ्रेड 2:

synchronized(foo) { 
    while(!condition) { 
     foo.wait(); 
    } 
} 

और पाठ्यक्रम का थ्रेड 1 सिंक्रनाइज़ किए गए ब्लॉक में सही स्थिति को सेट करता है। संकेत के लिए सप्ताहांत के लिए धन्यवाद!

क्या कोई मौजूदा वर्ग है जो उस व्यवहार को लपेटता है?

या मुझे कोड को कॉपी और पेस्ट करने की आवश्यकता है?

+0

क्या समस्या आप वास्तव में हल करने की कोशिश कर रहे हैं? जबकि मुझे यकीन है कि इस समस्या को हल किया जा सकता है, मैं अभी भी पूछना चाहूंगा ताकि हम जानते हैं कि यह आपकी समस्या का सही समाधान है। अच्छी तरह से समझने के दौरान धागे और सेमफोरों के साथ इस प्रकार की गड़बड़ी छोटी गलतियों के कारण असफलताओं के लिए प्रवण होती है, इसलिए आप जो हासिल करना चाहते हैं उसे करने के लिए एक और अधिक पढ़ने योग्य, समझने में आसान तरीका हो सकता है। विशेष रूप से यह एक साधारण Evenlistener पैटर्न की तरह लगता है जहां श्रोताओं घटनाओं के स्रोत के साथ रजिस्टर अधिक उपयुक्त होगा। – Thor84no

+0

हां, एक ईवेंट श्रोता थ्रेड 1 "पुश" अपडेट देकर समस्या का समाधान करेगा। यह आमतौर पर पसंदीदा विधि है, उदा। एक सर्वर वातावरण में। हालांकि, मैं समय-समय पर अपडेट के लिए थ्रेड-सुरक्षित "खींचने" की समस्या में भाग गया और अब तक मेरा कोई साफ समाधान नहीं था। – Kosta

उत्तर

18

सूचना देने पर कुछ राज्य बदलने के लिए मानक अभ्यास है और प्रतीक्षा करते समय कुछ राज्य जांचने के लिए()।

उदा।

boolean ready = false; 

// thread 1 
synchronized(lock) { 
    ready = true; 
    lock.notifyAll(); 
} 


// thread 2 
synchronized(lock) { 
    while(!ready) 
     lock.wait(); 
} 

इस दृष्टिकोण के साथ, इससे कोई फर्क नहीं पड़ता कि थ्रेड 1 या थ्रेड 2 पहले लॉक प्राप्त करता है या नहीं।

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

+0

हां, यही वह है। धन्यवाद! – Kosta

+2

इससे कोई डेडलॉक स्थिति नहीं होती है जहां थ्रेड 2 'लॉक' पकड़ लेता है और जब तक यह ब्लॉक छोड़ देता है तब तक रिलीज़ नहीं होगा (लेकिन यह नहीं छोड़ेगा, क्योंकि यह 'तैयार' होने के लिए इंतजार कर रहा है, जो कभी नहीं होगा जबकि थ्रेड 2 में 'लॉक' है)? – Thor84no

+3

'lock.wait() '' लॉक' जारी करता है और लौटने से पहले इसे फिर से प्राप्त करता है। –

2

सबसे आसान तरीका सिर्फ

firstThread.join();

यह जब तक पहले धागा समाप्त होता है अवरुद्ध हो जाएगा कहने के लिए है।

लेकिन आप प्रतीक्षा/अधिसूचना का उपयोग करके इसे लागू कर सकते हैं। दुर्भाग्यवश आपने अपने असली कोड टुकड़े पोस्ट नहीं किए हैं, लेकिन मुझे लगता है कि यदि आप सूचित करते हैं तो प्रतीक्षा बाहर नहीं निकलती है, ऐसा इसलिए होता है क्योंकि आपने synchronized ब्लॉक दोनों में नहीं रखा है। ध्यान दें कि synchronized ब्लॉक का "तर्क" जोड़ी को प्रतीक्षा/सूचित करने के लिए समान होना चाहिए।

+0

क्षमा करें, लेकिन इस मामले में, पहला थ्रेड अनिश्चित काल तक थोड़ी देर में चलता है, इसलिए firstThread.join() बस हमेशा के लिए चलता है। प्रतीक्षा करें() बाहर नहीं निकलता है क्योंकि इसे सूचित करने के बाद कहा जाता है सभी()। सिंक्रनाइज़ किए गए ब्लॉक और प्रतीक्षा()/सूचित करें सभी() सभी में एक ही तर्क हैं। – Kosta

3

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

synchronized(syncObject) { 
    while(condition.isTrue()) { 
     syncObject.wait(WAIT_TIMEOUT); 
    } 
} 

(अपने थ्रेड 2 में)

संपादित करें: ले जाया गया पाश बाहर सिंक्रनाइज़।

+0

समस्या यह है कि थ्रेड 2 पहले अधिसूचना को याद करता है(), एक टाइमआउट उस के खिलाफ मदद नहीं करता है। लेकिन: मैंने अपने प्रश्न में नकली जागरूकता को याद किया, इसलिए मुझे इसके लिए भी खाते की आवश्यकता है। हालत परिवर्तक वास्तव में मुझे चाहिए - लेकिन मुझे पसंद है (condition.isFalse()) – Kosta

+0

हां, आप निश्चित रूप से isalse() का उपयोग कर सकते हैं। इसके अलावा जबकि लूप सिंक्रनाइज़ अनुभाग के अंदर हो सकता है। यह कोड स्निपेट सिर्फ सामान्य विचार दिखाने के लिए है। – weekens

+0

इसके साथ समस्या यह है कि स्थिति जांचने के बाद सत्य बन सकती है लेकिन लॉक अधिग्रहण से पहले। इसका मतलब है कि आप किसी भी कारण से प्रतीक्षा() आईएनजी समाप्त करते हैं। –

2

मैं दो धागे के बीच BlockingQueue का उपयोग करता हूं। का उपयोग करते हुए wait और notify 5 मिनट पहले इतना है;)

enum Event { 
    Event, 
    Stop; 
} 

BlockingQueue<Event> queue = new LinkedBlockingQueue<Event>(); 

// Thread 1 
try { 
    while(true) { 
    ...do something... 
    queue.put(Event.Event); 
    ...wait for some condition that might never happen... 
    ... 
    } 
} finally { 
    // Tell other thread we've finished. 
    queue.put(Event.Stop}; 
} 

// Thread 2 
... 
switch (queue.take()) { 
    case Event: 
     ... 
     break; 

    default: 
     ... 
     break; 
} 
+0

यह काम करता है, लेकिन मुझे इसके बारे में क्या पसंद नहीं है यह है कि थ्रेड 1 को पता होना चाहिए कि इसके लिए कितने धागे इंतजार कर रहे हैं: यदि आपके पास एन थ्रेड हैं जो लेते हैं() आईएनजी, थ्रेड 1 को एन इवेंट्स रखने की आवश्यकता है – Kosta

+0

@ कोस्टा - क्या आप अपने प्रश्न में उस आवश्यकता पर विस्तार कर सकते हैं? मुझे यकीन है कि हम उस कार्यक्षमता को भी प्रदान कर सकते हैं। – OldCurmudgeon

+0

मुझे लगता है कि आप साइक्लिकबैरियर को भी देखना चाहेंगे - यह आपके द्वारा नामित सभी नौकरियों को संभालेगा और किसी भी लोलेवल कोड की भी आवश्यकता नहीं है :) - http://docs.oracle.com/javase/6/docs/api/ जावा/util/समवर्ती/CyclicBarrier.html –

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