2010-04-27 2 views
6

대기열에서 계속해서 읽는 스레드를 사용하고 있습니다.Java에서 루핑 스레드 중지 중

public void run() { 
    Object obj; 
    while(true) { 
     synchronized(objectsQueue) { 
      if(objectesQueue.isEmpty()) { 
       try { 
        objectesQueue.wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 

       obj = objectesQueue.poll(); 
      } 
     } 

     // Do something with the Object obj 
    } 
} 

이 스레드를 중지하는 가장 좋은 방법은 무엇입니까 : 같은

뭔가? - Thread.stop()는되지 않기 때문에, I는 N 원자 체크 조건 변수를 이용하는 stopThisThread() 방법을 구현할 수

1

I는 두 가지 옵션을 참조.

2 - Death Event 개체 또는 그와 비슷한 것을 큐에 보냅니다. 스레드가 종료 이벤트를 가져 오면 종료합니다.

나는 첫 번째 방법을 선호하지만 stopThisThread() 메서드를 호출해야 할 때를 알지 못합니다. 뭔가 큐에있을 수 있고 중지 신호가 먼저 도착할 수 있습니다 (바람직하지 않음).

제안 사항?

+3

주, 당신의'wait'는 의사를 깨우는을 방지하기 위해,'while' 루프가 아닌'if'에 있어야합니다. –

+0

인터럽트에 의존하는 모든 사람들에게 : 인생은 결코 단순하지 않으며 인터럽트가 아닌 "활동"에 인터럽트를 호출하고 그 활동이 인터럽트를 전혀 알지 못한다고 말하고 싶습니다. 이것은 경험에서 나온 것입니다. 그럼에도 불구하고, 나는 더 좋은 방법을 모른다 ... API를주의 깊게 보아라 : http://java.sun.com/javase/6/docs/api/index.html?java/lang/Thread .html 인터럽트() 메소드를 확인하십시오. – Yaneeve

+0

@halfwarp : Stephen C의 대답에 +1과 +1. 당신의 * "죽음 사건"*은 실제로 "독약 (poison pill)"이라고 불린다. * 일단 당신이 그것을 dequeue하면 "쓰레드 멈추기"를 의미하는 특별한 물건 (poison pill)을 엔큐하기 만하면된다. 그렇게하면 * "대기열로가는 길에있을 수 있습니다"* [sic]는 여전히 독약보다 먼저 처리됩니다. – SyntaxT3rr0r

답변

6

DeathEvent (또는 "poison pill"이라고도 함) 접근 방식은 종료하기 전에 대기열에서 모든 작업을 완료해야하는 경우 잘 작동합니다. 문제는 이것이 오랜 시간이 걸릴 수 있다는 것입니다.

가능한 한 빨리 중지하려면

, 나는 당신이 그 run 방법으로 인스턴스화 스레드 t를 중지하려면이

BlockingQueue<O> queue = ... 

... 

public void run() { 
    try { 
     // The following test is necessary to get fast interrupts. If 
     // it is replaced with 'true', the queue will be drained before 
     // the interrupt is noticed. (Thanks Tim) 
     while (!Thread.interrupted()) { 
      O obj = queue.take(); 
      doSomething(obj); 
     } 
    } catch (InterruptedException ex) { 
     // We are done. 
    } 
} 

을 제안, 단순히 t.interrupt();를 호출합니다.

위의 코드를 다른 답변과 비교하면 BlockingQueueThread.interrupt()을 사용하면 솔루션을 단순화하는 방법을 알 수 있습니다.

나는 또한 여분의 stop 플래그가 불필요하며 큰 그림에서는 잠재적으로 유해하다고 주장 할 것입니다. 잘 작동하는 작업자 스레드는 인터럽트를 존중해야합니다. 예기치 않은 인터럽트는 단순히 원래 프로그래머가 예상하지 못한 상황에서 작업자가 실행되고 있음을 의미합니다. 가장 좋은 방법은 작업자가 수행해야 할 작업을 수행하는 것입니다. 즉, 원래 프로그래머의 개념에 부합하는지 여부와 상관없이 중지해야합니다.

+0

@Stephen C : +1 ... JCIP 이외에도 예기치 않은 인터럽트가 발생하면 * if (중단됨) {Thread.currenThread(). interrupt()}를 사용하여 중단 된 상태를 복원하는 것이 좋습니다. } * * finally 블록에서 생성. Hmmmm 결국 더 좋은 방법은 예기치 않은 인터럽트를 받고 중단 된 상태를 복원하고 종료 된 다음 몇 가지 * stopThisThread() * 메소드를 사용하여 "깨끗한"정지를 허용하는 것입니다. – SyntaxT3rr0r

+0

@WizardOfOdds - 나는 당신을 팔로우하지 않습니다. 1)'run' 메소드가'interrupt()'를 호출하여 플래그를 복구 할 필요가 없습니다. 그것을 호출 한 스레드가 곧 죽습니다. 2) 방금 언급 한 내용이 왜 방해로 인한 중지가 나쁜지 또는 왜 "깨끗한"정지 메커니즘이 더 좋은지 설명합니다. (그리고 JCIP 권고안은 설명이 아닙니다.) –

+0

@Stephen C : JCIP 142쪽에 "설명"이 있습니다. Btw 나는 어떤 설명도하지 않았고 물음표와 함께 나의 코멘트를 끝내었다? 혹시 통지하지 못했을 경우 : – SyntaxT3rr0r

0

접근법 1이 선호됩니다.

휘발성 stop 필드를 true로 설정하고 실행중인 스레드에서 interrupt()을 호출하기 만하면됩니다. 이것은 InterruptedException을 리턴하기 위해 기다리는 모든 I/O 메소드를 강제 실행합니다 (라이브러리가 올바르게 작성된 경우 정상적으로 처리됩니다).

+2

유권자는이 답변의 문제점에 대해 의견을 낼 수 있습니까? –

+1

방금 ​​투표했습니다. 내 추론은 별도의 정지 필드를 사용할 필요가 없다는 것입니다. 동시성은 어려운 주제입니다. 기술적으로 잘못된 대답은 없지만 모범 사례에 적합하지 않으며 모범 사례에 부합하지 않는 답변의 노출을 커뮤니티의 관심으로 제한한다고 생각합니다. –

+0

@TimBender 나는 정중하게 의견이 달리합니다. 부울 필드를 설정하고 스레드를 인터럽트하는 큐 작업자 객체에 stop() 메서드를 사용하는 것은 모든 컨센서스에 의해 "모범 사례"입니다. – nsayer

0

두 사례가 실제로 같은 잠재적 인 행동을한다고 생각합니다. 두 번째 경우에는 스레드 A가 DeathEvent를 추가 한 후 스레드 B가 FooEvent를 추가하는 것으로 간주합니다. 작업 스레드가 DeathEvent를 받으면 그 뒤에는 여전히 FooEvent가 있습니다. 반환하기 전에 대기열을 지우려고하지 않는 한 옵션 1에서 설명한 것과 같은 시나리오입니다.하지만 실제로 스레드를 유지하고 있습니다. 그것을 막으려 고 노력하고 있습니다.

첫 번째 옵션이 더 바람직하다는 데 동의합니다. 잠재적 인 솔루션은 대기열이 채워지는 방식에 따라 다릅니다.

MyThread extends Thread{ 
    boolean running = true; 

    public void run(){ 
    while(running){ 
     try{ 
     //process queue... 
     }catch(InterruptedExcpetion e){ 
     ... 
     } 
    } 
    } 

    public void stopThisThread(){ 
    running = false; 
    interrupt(); 
    } 

    public boolean enqueue(Object o){ 
    if(!running){ 
     return false; 
     OR 
     throw new ThreadNotRunningException(); 
    } 
    queue.add(o); 
    return true; 
    } 
} 

그것은 다음과 같다 :이 작업 스레드 클래스의 일부분 인 경우에는 해당 stopThisThread을() 메소드는 대기열을 넣는 호출 예에서 적절한 값 (또는 던져 예외) 반환 플래그를 설정 할 수 이벤트를 적절하게 처리하기 위해 이벤트를 큐에 넣으려고하는 객체의 책임.하지만 적어도 이벤트가 대기열에 있지 않고 처리되지 않는다는 것을 알게됩니다.

+0

실행 변수를 휘발성 변수로 정의하지 않으면 접근 방식이 잘못되었습니다. 스레드를 확장하는 것이 아니라 호출 가능 또는 Runnable을 구현하여 범위에 인터럽트 기능을 구현하지 않도록하십시오. – Roman

0

나는 보통 쓰레드가있는 클래스에 깃발을 넣고, 쓰레드 코드에서는 그렇게 할 것이다. (참고 : while (true) 대신 while (flag))

그런 다음 플래그를 false로 설정하는 클래스의 메서드를 만듭니다.

private volatile bool flag = true; 

public void stopThread() 
{ 
    flag = false; 
} 

    public void run() { 
     Object obj; 
     while(flag) { 
      synchronized(objectsQueue) { 
       if(objectesQueue.isEmpty()) { 
        try { 
         objectesQueue.wait(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 

        obj = objectesQueue.poll(); 
       } 
      } 

      // Do something with the Object obj 
     } 
    } 
+0

스레드로부터 안전하지 않습니다. 'flag' 변수는 적절한 동기화없이 다른 스레드에서 읽고 쓰고 있습니다. –

+2

@Romain Hippeau : 부울 플래그 * volatile *을 만들었습니다. 그렇지 않으면 Stephen C가 지적했듯이 코드가 제대로 동기화되지 않았습니다. – SyntaxT3rr0r

+0

@WizardOfOdds - 수정 해 주셔서 감사합니다. –

1

리더 스레드에 부울 변수 정지가 있습니다. 이 스레드를 중지하고 thius를 true로 설정하고 스레드를 인터럽트하려는 경우 판독기 스레드 내에서 안전하지 않은 경우 (처리되지 않은 오브젝트가없는 경우) 정지 변수의 상태를 점검하고 설정된 경우 루프에서 리턴하십시오. 아래와 같이

public class readerThread extends Thread{ 
    private volitile boolean stop = false; 
    public void stopSoon(){ 
     stop = true; 
     this.interrupt(); 
    } 
    public void run() { 
     Object obj; 
     while(true) { 
      if(stop){ 
       return; 
      } 
      synchronized(objectsQueue) { 
      if(objectesQueue.isEmpty()) { 
       try { 
        objectesQueue.wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       if(stop){ 
        return; 
       }  
       obj = objectesQueue.poll(); 
       // Do something with the Object obj 
      } 
     } 
    } 


} 
public class OtherClass{ 
    ThreadReader reader; 
    private void start(){ 
      reader = ...; 
      reader.start(); 
    } 

    private void stop(){ 
      reader.stopSoon(); 
      reader.join();  // Wait for thread to stop if nessasery. 
    } 
} 
+0

이것은 내가 대기열 작업자를 고전적으로 코딩 한 방법입니다. 이 접근법과 수용된 대답의 중요한 차이점은 "포이즌 필"방법은 대기열이 버려지기보다는 완전히 종료되기 전에 처리된다는 것입니다. 이러한 접근법 중 어느 것이 더 정확한지는 문제 공간에 따라 다릅니다. thread()가 실제로 죽을 때까지 stopSoon()을 차단하게하는 interrupt() 다음에 join()을 추가하는 것도 고려할 수 있습니다. – nsayer

1

왜 필요할 때 간단히 중지 할 수있는 스케줄러를 사용하지 않으시겠습니까? 표준 스케쥴러는 새로운 실행을 다시 스케줄하기 전에 작업자 스레드가 완료되기를 기다리는 반복 스케줄링을 지원합니다.

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); 
service.scheduleWithFixedDelay(myThread, 1, 10, TimeUnit.SECONDS); 

이 샘플은 하나의 실행이 완료되면, 그것은 10 초 후에 다시 시작합니다 의미 10 초 지연으로 스레드를 실행됩니다. 그리고 바퀴를 재발 명할 필요없이

service.shutdown() 

while (true)는 더 이상 필요하지 않습니다.

ScheduledExecutorService Javadoc

+1

'service.shutdown()'은 poison-pill 접근법과 유사하다 : 더 이상의 작업은 받아 들여지지 않고 실행중인 작업은 계속 실행되며 이전에 수락 된 작업이 실행된다. 반면,'service.shutdownNow()'는 작업 실행을 중단하려고 시도하고 이전에 수락 된 작업은 실행되지 않습니다. – user454322