सक्रिय धागे की गिनती को बनाए रखें।
public class ThreadCounter {
public static final AtomicInteger threadCounter = new AtomicInteger(N);
public static final AtomicInteger queueCounter = new AtomicInteger(0);
public static final Object poisonPill = new Object();
public static volatile boolean cancel = false; // or use a final AomticBoolean instead
}
आपका धागे 'मतदान पाश की तरह निम्नलिखित (मैं यह सोचते कर रहा हूँ कि तुम एक BlockingQueue
उपयोग कर रहे हैं)
while(!ThreadCounter.cancel) {
int threadCount = ThreadCounter.threadCounter.decrementAndGet(); // decrement before blocking
if(threadCount == 0 && ThreadCounter.queueCounter.get() == 0) {
ThreadCounter.cancel = true;
queue.offer(ThreadCounter.poisonPill);
} else {
Object obj = queue.take();
ThreadCounter.threadCounter.incrementAndGet(); // increment when the thread is no longer blocking
ThreadCounter.queueCounter.decrementAndGet();
if(obj == ThreadCounter.poisonPill) {
queue.offer(obj); // send the poison pill back through the queue so the other threads can read it
continue;
}
}
}
एक धागा बारे में BlockingQueue
पर ब्लॉक करने के लिए तो यह decrements है दिखना चाहिए काउंटर; यदि सभी धागे पहले से ही कतार पर प्रतीक्षा कर रहे हैं (जिसका अर्थ है कि counter == 0
), तो अंतिम धागा cancel
को सत्य पर सेट करता है, फिर अन्य धागे को जागने के लिए कतार के माध्यम से एक जहर गोली भेजता है; प्रत्येक थ्रेड जहर की गोली को देखता है, शेष धागे को जागने के लिए इसे कतार के माध्यम से वापस भेजता है, और फिर यह देखता है कि cancel
सत्य पर सेट है।
संपादित करें: मैं एक queueCounter
कि कतार में वस्तुओं की संख्या की गिनती का कहना जोड़कर डेटा दौड़ हटा दिया है (जाहिर है आप भी भी आप जोड़ रहे हैं वस्तुओं के लिए एक queueCounter.incrementAndGet()
कॉल जोड़ने की आवश्यकता होगी कतार में)। यह निम्नानुसार काम करता है: यदि threadCount == 0
, लेकिन queueCount != 0
, तो इसका मतलब है कि थ्रेड ने कतार से एक आइटम हटा दिया है, लेकिन अभी तक threadCount.getAndIncrement
नहीं कहा गया है, और इसलिए रद्द चर सत्य पर सेट नहीं है। यह महत्वपूर्ण है कि threadCount.getAndIncrement
कॉल queueCount.getAndDecrement
कॉल से पहले कॉल करें, अन्यथा आपके पास अभी भी डेटा रेस होगी। इससे कोई फर्क नहीं पड़ता कि आप queueCount.getAndIncrement
पर किस ऑर्डर को कॉल करते हैं, क्योंकि आप threadCount.getAndDecrement
पर कॉल के साथ इसे अंतःस्थापित नहीं करेंगे (बाद वाले को लूप के अंत में बुलाया जाएगा, पूर्व को लूप की शुरुआत में बुलाया जाएगा)।
ध्यान दें कि प्रक्रिया को समाप्त करने का निर्धारण करने के लिए आप केवल queueCount
का उपयोग नहीं कर सकते हैं, क्योंकि थ्रेड अभी भी कतार में कोई भी डेटा रखे बिना सक्रिय हो सकता है - दूसरे शब्दों में, queueCount
शून्य होगा, लेकिन होगा एक बार धागे ने अपने वर्तमान पुनरावृत्ति को समाप्त करने के बाद शून्य न हो।
कतार के माध्यम से बार-बार poisonPill
भेजने के बजाय, आप कतार के माध्यम से रद्द करने के थ्रेड को भेज सकते हैं (एन -1) poisonPills
। अगर आप एक अलग कतार का उपयोग करके इस दृष्टिकोण का उपयोग करते हैं तो सावधान रहें, क्योंकि कुछ कतार (जैसे अमेज़ॅन की सरल कतार सेवा) अपने take
विधियों के बराबर कई आइटम लौटा सकती है, इस मामले में आपको बार-बार poisonPill
को सुनिश्चित करने की आवश्यकता होगी कि सब कुछ बंद हो जाता है।
साथ ही, बजाय एक while(!cancel)
पाश का उपयोग कर के, आप एक while(true)
पाश का उपयोग करें और जब पाश एक poisonPill
मैं अनुमान रद्द अस्थिर होना चाहिए का पता लगाता है तोड़ सकते हैं? – marcorossi
क्या होता है जब कॉल के बीच queue.take() और incrementAndGet(), और अन्य थ्रेड कॉल के बीच होता है तो गिनती == 0 और queue.isEmpty() स्थिति का मूल्यांकन कर रहा है? – marcorossi
@marcorossi हाँ 'रद्द करें' अस्थिर होना चाहिए ताकि परिवर्तन सभी धागे में दिखाई दे। उदाहरण के लिए सभी धागे 'रद्द' फ़ील्ड में सबसे हालिया लेखन देखते हैं, यह पढ़ने और लिखने की गारंटी देता है परमाणु हैं। जवाब संपादित किया है। – adamjmarkham