take ()를 차단하는 BlockingQueue를 중단하는 방법은 무엇입니까?


82

a에서 개체를 가져와 연속 루프에서 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에 의해 인식되고 루프를 벗어나는 큐에 "마커"또는 "명령"개체를 배치하는 것입니다.


39
이것은 'Poison Pill Shutdown'접근 방식으로도 알려져 있으며 "Java Concurrency in Practice", 특히 pp. 155-156에서 자세히 설명합니다.
Brandon Yarbrough

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> while (queue.size ()> 0) Thread.currentThread (). sleep (5000); </ code>
MCS

3
@MCS-향후 방문자를 위해 여기에 대한 접근 방식은 해킹이며 세 가지 이유로 프로덕션 코드에서 재현해서는 안된다는 점에 유의하십시오. 종료를 연결하는 실제 방법을 찾는 것이 항상 선호됩니다. Thread.sleep()적절한 후크의 대안으로 사용 하는 것은 결코 용납되지 않습니다 . 다른 구현에서는 다른 스레드가 항목을 대기열에 배치 할 수 있으며 while 루프는 종료되지 않을 수 있습니다.
Erick Robertson

고맙게도 이러한 해킹에 의존 할 필요가 없습니다 . 필요한 경우 인터럽트 후에 쉽게 처리 할 수 ​​있기 때문 입니다. 예를 들어, "완전한" take()구현은 다음과 같을 수 있습니다. try { return take(); } catch (InterruptedException e) { E o = poll(); if (o == null) throw e; Thread.currentThread().interrupt(); return o; } 그러나이 레이어에서 구현해야 할 이유가 없으며 약간 더 높은 수준으로 구현하면 더 효율적인 코드로 이어질 것입니다 (예 : 요소 별 InterruptedException및 / 또는 )를 사용하여 BlockingQueue.drainTo().
antak

13

매우 늦었지만 비슷한 문제에 직면하고 위의 erickson이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
내가 착각하지 않으면 대기열의 마지막 요소가 처리되지 않습니다. 큐에서 최종 요소를 가져 오면 finished는 true이고 큐는 비어 있으므로 해당 최종 요소를 처리하기 전에 반환됩니다. 세 번째 조건 추가 if (Finished && queue.isEmpty () && obj == null)
Matt R

@MattR 감사합니다, 게시 된 답변을 편집 할 것이라고 올바르게 조언했습니다
dbw


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. 루프 while (! queue.isDone () ||! queue.isEmpty ())
  3. null에 대한 take () 반환 값 테스트

1
그 이전의 솔루션은 깨끗했다 말하고 것이다 간단한 이것보다
sakthisundar

1
완료 플래그는 독약, 그 그냥 :) 다르게 관리와 동일합니다
데이빗 만

청소기? 나는 그것을 의심한다. 스레드가 정확히 언제 중단되는지 알 수 없습니다. 아니면 그게 무엇보다 더 깨끗한가요? 코드가 적다는 것은 사실입니다.
tomasb 2013

0

어떨까요

queue.add(new MyObj())

일부 생산자 스레드에서 stop-flag가 소비자 스레드에게 while-loop를 종료하도록 신호를 보내는 곳?


1
수락 된 답변 (녹색을 찾으세요 ✓)과 다른 답변이있는 이전 질문에 답변하기 전에 답변이 새로운 내용을 추가하거나 그와 관련하여 도움이되는지 확인하십시오. 따라서 귀하의 질문에 대한 답변 은 OP의 질문에 대한 답변으로 제공됩니다. – Stack Overflow의 Q / A 형식은 포럼 스타일의 토론을위한 것이 아닙니다. "X는 어때?"라고 묻지 마세요. OP의 질문에 대답하지 않고 오히려 주석입니다. 기여자 지침을 참조하십시오 .
Ivo Mori

... 본, 내 메시지가 제거 될 수 있습니다
Sam Ginrich
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.