BlockingQueue को कैसे बाधित करें जो टेक () पर रोक रहा है?


82

मेरे पास एक वर्ग है जो वस्तुओं को लेता है BlockingQueueऔर take()एक सतत लूप में कॉल करके उन्हें संसाधित करता है । कुछ बिंदु पर मुझे पता है कि कतार में कोई और ऑब्जेक्ट नहीं जोड़ा जाएगा। मैं take()विधि को कैसे बाधित करूं ताकि यह अवरुद्ध होना बंद हो जाए?

यहाँ वह वर्ग है जो वस्तुओं को संसाधित करता है:

public class MyObjHandler implements Runnable {

  private final BlockingQueue<MyObj> queue;

  public class MyObjHandler(BlockingQueue queue) {
    this.queue = queue;
  }

  public void run() {
    try {
      while (true) {
        MyObj obj = queue.take();
        // process obj here
        // ...
      }
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
  }
}

और यहाँ वह विधि है जो वस्तुओं को संसाधित करने के लिए इस वर्ग का उपयोग करती है:

public void testHandler() {

  BlockingQueue<MyObj> queue = new ArrayBlockingQueue<MyObj>(100);  

  MyObjectHandler  handler = new MyObjectHandler(queue);
  new Thread(handler).start();

  // get objects for handler to process
  for (Iterator<MyObj> i = getMyObjIterator(); i.hasNext(); ) {
    queue.put(i.next());
  }

  // what code should go here to tell the handler
  // to stop waiting for more objects?
}

जवाबों:


72

यदि थ्रेड को बाधित करना एक विकल्प नहीं है, तो दूसरा उस कतार पर "मार्कर" या "कमांड" ऑब्जेक्ट को रखना है जो MyObjHandler द्वारा इस तरह के रूप में पहचाना जाएगा और लूप से बाहर निकलेगा।


40
इसे 'पॉइज़न पिल शटडाउन' दृष्टिकोण के रूप में भी जाना जाता है और "जावा कॉनसेरी इन प्रैक्टिस" की लंबाई पर चर्चा की जाती है, विशेष रूप से पीपी 155-156 पर।
ब्रैंडन यारब्राउट

14
BlockingQueue<MyObj> queue = new ArrayBlockingQueue<MyObj>(100);
MyObjectHandler handler = new MyObjectHandler(queue);
Thread thread = new Thread(handler);
thread.start();
for (Iterator<MyObj> i = getMyObjIterator(); i.hasNext(); ) {
  queue.put(i.next());
}
thread.interrupt();

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


हाँ, यह एक समस्या है अगर धागा बाधित है जबकि कतार में अभी भी आइटम हैं। इसके चारों ओर जाने के लिए, मैंने यह सुनिश्चित करने के लिए कोड जोड़ा कि कतार थ्रेड को बाधित करने से पहले खाली है: <code> जबकि (queue.size ()> 0) Thread.currentThread ()। Sleep (5000); </ code>
MCS

3
@MCS - भविष्य के आगंतुकों के लिए ध्यान दें कि यहां आपका दृष्टिकोण एक हैक है और इसे तीन कारणों से उत्पादन कोड में पुन: प्रस्तुत नहीं किया जाना चाहिए। हमेशा शटडाउन को हुक करने का एक वास्तविक तरीका खोजना पसंद किया जाता है। यह Thread.sleep()उचित हुक के विकल्प के रूप में उपयोग करने के लिए कभी भी स्वीकार्य नहीं है । अन्य कार्यान्वयन में, अन्य धागे चीजों को कतार में रख सकते हैं, और जबकि लूप कभी समाप्त नहीं हो सकता है।
एरिक रॉबर्टसन

शुक्र है, इस तरह के हैक पर भरोसा करने की कोई जरूरत नहीं है क्योंकि एक तो बस आवश्यक होने पर बीच में आने के बाद आसानी से इसे संसाधित कर सकता है। उदाहरण के लिए, एक "संपूर्ण" take()कार्यान्वयन इस तरह दिखाई दे सकता है: try { return take(); } catch (InterruptedException e) { E o = poll(); if (o == null) throw e; Thread.currentThread().interrupt(); return o; } हालाँकि, इस परत पर लागू होने के लिए कोई कारण नहीं है, और इसे थोड़ा अधिक लागू करने से अधिक कुशल कोड प्राप्त होगा (जैसे कि प्रति-तत्व InterruptedExceptionऔर / या से बचकर) का उपयोग करके BlockingQueue.drainTo())।
मारक

13

बहुत देर से लेकिन आशा है कि इससे अन्यों को भी मदद मिलेगी क्योंकि मैंने भी इसी तरह की समस्या का सामना किया था और ऊपर दिएpoll गए दृष्टिकोण का इस्तेमाल कुछ छोटे बदलावों के साथ किया।

class MyObjHandler implements Runnable 
{
    private final BlockingQueue<MyObj> queue;
    public volatile boolean Finished;  //VOLATILE GUARANTEES UPDATED VALUE VISIBLE TO ALL
    public MyObjHandler(BlockingQueue queue) 
    {
        this.queue = queue;
        Finished = false;
    }
    @Override
    public void run() 
    {        
        while (true) 
        {
            try 
            {
                MyObj obj = queue.poll(100, TimeUnit.MILLISECONDS);
                if(obj!= null)//Checking if job is to be processed then processing it first and then checking for return
                {
                    // process obj here
                    // ...
                }
                if(Finished && queue.isEmpty())
                    return;

            } 
            catch (InterruptedException e) 
            {                   
                return;
            }
        }
    }
}

public void testHandler() 
{
    BlockingQueue<MyObj> queue = new ArrayBlockingQueue<MyObj>(100); 

    MyObjHandler  handler = new MyObjHandler(queue);
    new Thread(handler).start();

    // get objects for handler to process
    for (Iterator<MyObj> i = getMyObjIterator(); i.hasNext(); )
    {
        queue.put(i.next());
    }

    // what code should go here to tell the handler to stop waiting for more objects?
    handler.Finished = true; //THIS TELLS HIM
    //If you need you can wait for the termination otherwise remove join
    myThread.join();
}

इससे दोनों समस्याएं हल हो गईं

  1. को हरी झंडी दिखाई BlockingQueue ताकि यह पता कि इसे तत्वों के लिए अधिक इंतजार नहीं करना पड़ा है
  2. बीच में बाधित नहीं हुआ ताकि प्रसंस्करण ब्लॉक केवल तभी समाप्त हो जाएं जब कतार में सभी आइटम संसाधित हो जाएं और कोई आइटम शेष न हों

2
धागे के बीच दृश्यता की गारंटी के लिए Finishedचर बनाएं volatile। देखें stackoverflow.com/a/106787
lukk

2
यदि मुझसे गलती नहीं हुई है, तो कतार में अंतिम तत्व संसाधित नहीं किया जाएगा। जब आप कतार से अंतिम तत्व लेते हैं, तो समाप्त हो चुका है और कतार खाली है, इसलिए यह अंतिम तत्व को संभालने से पहले वापस आ जाएगा। एक तीसरी शर्त जोड़ें अगर (समाप्त && queue.isEmpty () && obj == null)
मैट आर

@ मैटर धन्यवाद, सही ढंग से सलाह दी गई है कि मैं पोस्ट किए गए उत्तर को संपादित
करूंगा


0

या बाधित नहीं है, इसका बुरा।

    public class MyQueue<T> extends ArrayBlockingQueue<T> {

        private static final long serialVersionUID = 1L;
        private boolean done = false;

        public ParserQueue(int capacity) {  super(capacity); }

        public void done() { done = true; }

        public boolean isDone() { return done; }

        /**
         * May return null if producer ends the production after consumer 
         * has entered the element-await state.
         */
        public T take() throws InterruptedException {
            T el;
            while ((el = super.poll()) == null && !done) {
                synchronized (this) {
                    wait();
                }
            }

            return el;
        }
    }
  1. जब निर्माता कतार में वस्तु डालता है queue.notify(), तो कॉल करें , अगर यह समाप्त हो जाए, तो कॉल करेंqueue.done()
  2. लूप जबकि (कतार ।isDone () ||! queue.isEmpty ())
  3. null के लिए test take () रिटर्न वैल्यू

1
मैं कहूंगा कि पिछले समाधान एक क्लीनर और इस से भी सरल था
sakthisundar

1
किया गया झंडा एक जहर की गोली के समान है, इसके अलग तरीके से प्रशासित :)
डेविड मैन

सफाई वाला? मुझे शक है। आप नहीं जानते कि जब धागा बिल्कुल बाधित हो। या मुझे पूछना, उस के साथ क्लीनर क्या है? इसका कम कोड, यह एक तथ्य है।
तोमसब

0

क्या एक के बारे में

queue.add(new MyObj())

कुछ प्रोड्यूसर थ्रेड में, जहां एक स्टॉप-फ्लैग उपभोक्ता के धागे को लूप को समाप्त करने का संकेत देता है?


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

... देखा, मेरा संदेश हटाया जा सकता है
सैम गिन्नरिच
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.