Java에서 InterruptedException 처리


317

다음 처리 방법의 차이점은 무엇입니까 InterruptedException? 가장 좋은 방법은 무엇입니까?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

또는

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

편집 : 나는이 두 시나리오가 어떤 시나리오에서 사용되는지 알고 싶습니다.


답변:


436

다음 InterruptedException 처리 방법의 차이점은 무엇입니까? 가장 좋은 방법은 무엇입니까?

던지는 메소드를 호출했기 때문에 아마도이 질문을했을 것입니다 InterruptedException.

우선, throws InterruptedException그 의미가 무엇인지 알아야합니다. 메소드 서명의 일부 및 호출중인 메소드 호출의 결과. 따라서 InterruptedExceptiona가 메소드 호출의 완벽한 결과 라는 사실을 받아들이는 것으로 시작 하십시오.

방법 당신이있는 거 호출은 예외가 발생하면 지금, 무엇을해야 당신의 방법은 무엇입니까? 다음에 대해 생각하여 답을 찾을 수 있습니다.

이 방법에 대한 이해 지나요 당신이 을 던져 구현됩니다 InterruptedException? 다르게 말하면, 메소드를 InterruptedException호출 때 합리적인 결과 입니까?

  • 경우 , 다음 throws InterruptedException의 일부가되어야 당신의 방법 서명, 당신은 (즉, 전혀 그것을 잡을하지 않음) 예외 전파를하도록해야한다.

    예제 : 메소드는 네트워크의 값이 계산을 완료하고 결과를 반환하기를 기다립니다. 네트워크 호출 차단이 발생하면 InterruptedException메소드가 정상적인 방식으로 계산을 완료 할 수 없습니다. 당신은 할 수 InterruptedException전파를.

    int computeSum(Server server) throws InterruptedException {
        // Any InterruptedException thrown below is propagated
        int a = server.getValueA();
        int b = server.getValueB();
        return a + b;
    }
  • 경우 에는 , 당신은 귀하의 방법을 선언하지해야합니다 throws InterruptedException그리고 당신은 (필수!) 예외를 catch해야합니다. 이제이 상황에서 두 가지를 명심해야합니다.

    1. 누군가 스레드를 중단했습니다. 누군가가 작업을 취소하고 프로그램을 정상적으로 종료하는 등의 일을 원할 수도 있습니다. 당신은 그 사람에게 공손하고 더 이상 고민하지 않고 당신의 방법에서 돌아와야합니다.

    2. 비록 당신의 방법은 경우에 합리적인 반환 값을 생성 관리 할 수있는 InterruptedException스레드가 여전히 중요 할 수 있습니다 중단 된 사실. 특히, 메소드를 호출하는 코드는 메소드 실행 중에 중단이 발생했는지 여부에 관심이있을 수 있습니다. 따라서 중단 된 플래그를 설정하여 중단이 발생했음을 기록해야합니다.Thread.currentThread().interrupt()

    : 사용자가 두 값의 합계를 인쇄하도록 요청했습니다. Failed to compute sum합계를 계산할 수없는 경우 " " 인쇄 가 가능합니다 (및로 인해 스택 추적으로 프로그램이 충돌하는 것보다 훨씬 낫습니다 InterruptedException). 다시 말해, 이 메소드를로 선언하는 것은 의미 가 없습니다throws InterruptedException .

    void printSum(Server server) {
         try {
             int sum = computeSum(server);
             System.out.println("Sum: " + sum);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();  // set interrupt flag
             System.out.println("Failed to compute sum");
         }
    }

이제는 그냥하는 throw new RuntimeException(e)것이 나쁜 생각 임을 분명히해야합니다 . 발신자에게는 그다지 예의가 없습니다. 새로운 런타임 예외를 만들 수는 있지만 근본 원인 (누군가 스레드가 실행을 중지하도록하려는 경우)이 손실 될 수 있습니다.

다른 예 :

구현Runnable : 발견했듯이 서명은 다시 Runnable.run던질 수 없습니다 InterruptedExceptions. 글쎄, 당신은 구현에 가입 Runnable하는 것이 어떤 의미, 당신이 할 수와 계약에 서명 InterruptedExceptions. 와 같은 다른 인터페이스를 선택 Callable하거나 위의 두 번째 방법을 따르십시오.

 

호출Thread.sleep : 파일을 읽으려고하는데 1 초 동안 10 번 시도해야한다고 사양에 명시되어 있습니다. 당신은 전화 Thread.sleep(1000). 따라서을 처리해야합니다 InterruptedException. 같은 방법 tryToReadFile은, 말을 완벽하게 말이 "나는 중단하고있어, 내가 파일을 읽으려고 내 작업을 완료 할 수 없습니다" . 다시 말해, 메소드가 throw하는 것이 완벽하게 이해됩니다 InterruptedExceptions.

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

이 게시물은 여기 기사로 다시 작성되었습니다 .


두 번째 방법의 문제점은 무엇입니까?
blitzkriegz

4
이 기사에서는 interrupt()중단 상태를 유지하기 위해 전화해야한다고 말합니다 . 에 이것을하지 않는 이유는 무엇입니까 Thread.sleep()?
oksayt

7
스레드가 인터럽트를 지원하지 않는 경우 수신되는 인터럽트가 예기치 않은 조건 (예 : 프로그래밍 오류, 잘못된 API 사용)을 나타내며 RuntimeException과의 격차가 적절한 응답 (실패)이라는 것에 동의하지 않습니다 ). 스레드의 일반 계약의 일부로 간주되지 않는 한 인터럽트를 지원하지 않더라도 인터럽트를받을 때 작업을 계속해야합니다.
amoe

12
InterruptedExceptions를 발생시키고 "인터럽트를 지원하지 않는다"고 말하는 메소드를 호출하는 것은 조잡한 프로그래밍과 버그가 발생하는 것처럼 보입니다. 다르게 넣으십시오. InterruptedException을 발생 시키도록 선언 된 메소드를 호출하는 경우 해당 메소드가 실제로 이러한 예외를 발생시키는 경우 예상치 못한 조건 이되어서는 안됩니다 .
aioobe

1
@MartiNito, 아니, 전화 Thread.currentThread.interrupt()에서 InterruptedExceptioncatch 블록 만 인터럽트 플래그를 설정하지 않습니다. InterruptedException외부 InterruptedException캐치 블록 에 다른 것을 던지거나 잡히지 않습니다 .
aioobe

97

오늘 아침에 Brian Goetz의 Java Concurrency In Practice 에서 작업하는 방법에 대해 오늘 아침에 대해 읽었습니다 . 기본적으로 그는 세 가지 중 하나를 수행해야한다고 말합니다.

  1. 을 전파InterruptedException - 체크 던져 당신의 방법을 선언 InterruptedException발신자가 처리해야 너무.

  2. 인터럽트 복원 — 때때로 던질 수 없습니다 InterruptedException. 이 경우 호출 스택의 상위 코드에서 인터럽트가 발행되었음을 확인하고 메소드에서 신속하게 리턴 할 수 있도록 메소드를 InterruptedException호출하여 인터럽트 상태를 파악 하고 복원 해야합니다 . 참고 :이 방법은 분석법에 "시도"또는 "최선의 노력"시맨틱이있는 경우에만 적용 가능합니다. 즉, 분석법이 목표를 달성하지 못하면 중요한 것은 발생하지 않습니다. 예를 들어, 또는 그러한 방법 일 수도 있지만 그렇지 않을 수도 있습니다 . 자세한 내용은 여기 를 참조하십시오.interrupt()currentThreadlog()sendMetric()boolean tryTransferMoney()void transferMoney()

  3. 메소드 내에서 중단을 무시하지만 구아바를 통해 종료시 상태를 복원하십시오Uninterruptibles . UninterruptiblesJCIP § 7.1.3의 취소 불가능 작업 예와 같이 상용구 코드를 인수합니다.

10
"때때로 InterruptedException을 던질 수 없습니다" -때로는 메서드가 InterruptedExceptions를 전파하는 것이 적합하지 않다고 말합니다. 귀하의 배합 할 때마다 당신이 예외 : InterruptedException를 다시 던지는 것을 제안 할 것으로 보인다 수 있습니다 .
aioobe

1
그리고 7 장을 읽으면 Listing 7.7과 같은 미묘한 부분이 있다는 것을 알 수있다. 여기서 취소 할 수없는 작업은 인터럽트를 즉시 복원하지 않고 완료된 후에 만 ​​가능하다. 또한 Goetz는 그 책에 대한 많은 공동 저자를 가지고 있습니다.
Fizz

1
나는 그것이 간결하기 때문에 당신의 대답을 좋아하지만, 두 번째 경우에는 당신의 기능에서 부드럽고 신속하게 돌아와야한다고 주장합니다. 중간에 Thread.sleep ()이있는 긴 (또는 무한한) 루프를 상상해보십시오. 예외 : InterruptedException의 캐치 (). 인터럽트를 ()는 Thread.currentThread를 호출해야 하고 루프의 탈출.
Yann Vo

@YannVo 귀하의 제안을 적용하기 위해 답변을 편집했습니다.
leventov

18

무엇을하려고합니까?

InterruptedException스레드가 대기 또는 수면과 다른 스레드 인터럽트는 사용할 때 발생합니다 interrupt클래스의 방법을 Thread. 따라서이 예외를 발견하면 스레드가 중단되었음을 의미합니다. Thread.currentThread().interrupt();다른 곳에서 스레드의 "중단 된"상태를 확인하지 않는 한 일반적으로 다시 호출 할 필요가 없습니다.

를 던질 수있는 다른 옵션에 관해서 RuntimeException는 (현명한 사람은 누구입니까? 어떻게 처리합니까?)하는 것이 현명하지 않은 것처럼 보이지만 추가 정보없이 더 많은 것을 말하기는 어렵습니다.


14
호출 Thread.currentThread().interrupt()은 인터럽트 된 플래그 (다시)를 설정하는데, 이는 인터럽트가 더 높은 수준에서인지되고 처리되도록하려는 경우에 유용합니다.
Péter Török

1
@ 피터 : 나는 이것을 언급하기 위해 대답을 업데이트하고있었습니다. 감사.
Grodriguez

12

나에게 중요한 것은 InterruptedException이 잘못되지 않는다는 것입니다. 그것은 당신이 지시 한 것을 수행하는 스레드입니다. 따라서 RuntimeException에 싸서 다시 던지는 것은 의미가 없습니다.

많은 경우에, 당신이 말할 때 RuntimeException에 싸여진 예외를 다시 던지는 것이 합리적입니다. 내가 가진 응용 프로그램 전체의 예외 처리기를 기록하여 기록 할 수 있습니다. 그것은 InterruptedException의 경우가 아니며, 인터럽트 ()가 호출 된 것에 응답하는 스레드 일뿐입니다. 적시에 스레드의 처리를 취소하는 데 도움을주기 위해 InterruptedException이 발생합니다.

따라서 InterruptedException을 전파하거나 지능적으로 먹어야하며 (의미 한 것을 수행 할 장소에서 의미) 인터럽트 플래그를 재설정하십시오. InterruptedException이 발생하면 인터럽트 플래그가 지워집니다. Jdk 라이브러리 개발자가 가정하는 것은 예외를 처리하는 것이 예외를 처리하는 것으로 가정하므로 기본적으로 플래그가 지워집니다.

따라서 첫 번째 방법이 더 낫습니다. 문제에 게시 된 두 번째 예제는 스레드가 실제로 중단 될 것으로 예상하지 않고 중단하는 것이 오류가 아니라면 유용하지 않습니다.

다음은 인터럽트가 작동하는 방식을 설명하는 예제 입니다. 예제 코드에서 InterruptedException을 사용하여 Runnable의 run 메소드에서 while 루프를 구제하는 위치를 볼 수 있습니다.


10

올바른 기본 선택은 던지기 목록에 InterruptedException을 추가하는 것입니다. 인터럽트는 다른 스레드가 스레드를 끝내기를 원한다는 것을 나타냅니다. 이 요청의 이유는 명백하지 않으며 전적으로 상황에 따라 달라 지므로 추가 지식이 없으면 친절하게 종료 된 것으로 가정하고 종료를 피하는 것은 비 친화적 응답이라고 가정해야합니다.

Java는 무작위로 InterruptedException을 던지지 않습니다. 모든 조언은 응용 프로그램에 영향을 미치지 않지만 개발자가 "삼키기"전략을 따르는 것이 매우 불편한 경우가 있습니다. 한 팀이 많은 테스트를 개발하고 Thread.Sleep을 많이 사용했습니다. 이제 CI 서버에서 테스트를 시작했으며 때로는 코드 결함으로 인해 영구 대기 상태에 빠질 수 있습니다. 상황을 악화시키기 위해 CI 작업을 취소하려고 할 때 테스트를 중단하려는 Thread.Interrupt가 작업을 중단하지 않았으므로 CI 작업을 닫지 않았습니다. 상자에 로그인하고 프로세스를 수동으로 종료해야했습니다.

간단히 말해 간단히 InterruptedException을 던지면 스레드가 끝내야하는 기본 의도와 일치합니다. InterruptedException을 던지기 목록에 추가 할 수 없다면 RuntimeException에 래핑합니다.

더 나은 "기본"처리를 장려하기 때문에 InterruptedException이 RuntimeException 자체가되어야한다는 매우 합리적인 주장이 있습니다. 디자이너가 RuntimeException이 코드의 오류를 나타내야한다는 범주 형 규칙을 고수했기 때문에 RuntimeException이 아닙니다. InterruptedException은 코드의 오류로 인해 직접 발생하지 않으므로 그렇지 않습니다. 그러나 실제로 코드에 오류 (예 : 무한 루프, 교착 상태)가 있기 때문에 InterruptedException이 종종 발생하며 Interrupt는 해당 오류를 처리하는 다른 스레드의 방법입니다.

합리적인 정리가 필요하다는 것을 알고 있다면 그렇게하십시오. 인터럽트의 더 깊은 원인을 알고 있다면보다 포괄적 인 처리를 수행 할 수 있습니다.

따라서 요약하면 취급 선택은이 목록을 따라야합니다.

  1. 기본적으로 던지기에 추가하십시오.
  2. 던지기에 추가 할 수 없으면 RuntimeException (e)을 던지십시오. (여러 가지 나쁜 옵션 중 최고의 선택)
  3. 인터럽트의 명백한 원인을 알고있는 경우에만 원하는대로 처리하십시오. 처리가 메서드에 로컬 인 경우 Thread.currentThread (). interrupt ()를 호출하여 인터럽트를 다시 설정하십시오.

3

나는 대부분의 사람들과 기사가 언급 한 것에 마지막 옵션을 추가하고 싶었습니다. mR_fr0g에서 언급했듯이 다음 중 하나를 수행하여 인터럽트를 올바르게 처리하는 것이 중요합니다.

  • InterruptException 전파

  • 스레드에서 인터럽트 상태 복원

또는 추가적으로 :

  • 인터럽트 사용자 정의 처리

상황에 따라 사용자 정의 방식으로 인터럽트를 처리하는 데 아무런 문제가 없습니다. 인터럽트는 강력한 명령과는 달리 종료 요청이므로 응용 프로그램이 요청을 정상적으로 처리 할 수 ​​있도록 추가 작업을 완료하는 것이 완벽하게 유효합니다. 예를 들어, 스레드가 휴면 중이거나 IO 또는 하드웨어 응답을 기다리는 경우 인터럽트를 수신하면 스레드를 종료하기 전에 모든 연결을 정상적으로 닫는 것이 완벽하게 유효합니다.

주제를 이해하는 것이 좋습니다. 그러나이 기사는 다음과 같은 정보의 좋은 소스입니다. http://www.ibm.com/developerworks/java/library/j-jtp05236/


0

어떤 경우에는 아무것도하지 않아도 괜찮습니다. 아마도 기본적으로 수행 해야하는 작업은 아니지만 인터럽트가 발생할 방법이 없어야 할 경우 다른 조치를 취할지 잘 모르겠습니다 (아마도 오류를 기록하지만 프로그램 흐름에는 영향을 미치지 않습니다).

한 가지 경우는 작업 (차단) 대기열이있는 경우입니다. 이 작업을 처리하는 데몬 스레드가 있고 스레드를 직접 방해하지 않는 경우 (jvm이 jvm 종료시 데몬 스레드를 방해하지 않음) 인터럽트가 발생할 수있는 방법을 찾지 못하므로 발생할 수 있습니다. 그냥 무시했습니다. (데몬 스레드는 언제든지 jvm에 의해 종료 될 수 있으므로 어떤 경우에는 적합하지 않습니다.)

편집 : 다른 사례는 적어도 Oracle의 자습서를 기반으로합니다 : http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html


이것은 나쁜 조언입니다. 인터럽트를 무시하는 데 중요한 작업을 생각하는 데 극도로 어려움이 있습니다. 오라클은 게으르고 Thread.sleep () 호출에 혼란을 더하고 싶지 않았습니다. 스레드가 중단 된 이유를 알지 못하는 경우 수행중인 작업을 중지해야합니다 (가능한 한 빨리 스레드가 죽도록 돕기 위해 예외를 다시 반환하거나 다시 발생시켜야 함).
Ajax

그래서 내가 때때로 말한 이유입니다. 또한 그것은 아직 제안되지 않았고 어떤 경우에는 제 의견으로 는 완벽하게 좋습니다 . 어떤 종류의 소프트웨어를 빌드 하느냐에 달려 있다고 생각하지만 (JVM 종료를 제외하고) 절대로 멈추지 않아야하는 24/7 작업자 스레드가있는 경우 인터럽트를 처리합니다. 예를 들어 BlockingQueue의 항목을 오류로 처리합니다. (예 : 로그) "발생하지 않아야 함"이후 다시 시도하십시오. cource 중에서 프로그램 종료를 확인하는 별도의 플래그가 있습니다. 내 프로그램 중 일부에서 JVM 종료는 정상적인 작동에서 발생하는 것이 아닙니다.
Bjarne Boström

또는 다른 방법으로 말하면 : 내 생각에 인터럽트 예외로 인해 중지하기 위해 24/7을 실행 해야하는 응용 프로그램을 갖는 것보다 추가 오류 로그 문 (예 : 메일을 오류 로그로 보낸 것)이 위험에 처하는 것이 낫습니다. 정상적인 조건에서는 발생하지 않습니다.
Bjarne Boström

흠, 사용 사례가 다를 수 있지만 이상적인 세상에서는 중단되어 종료되지 않습니다. 대기열에서 작업을 중단하고 스레드가 죽게합니다. 스레드 스케줄러도 중단되어 종료하라는 메시지가 표시되면 JVM이 종료되고 게임이 종료됩니다. 특히 kill -9를 보내거나 다른 방법으로 메일 서버를 중단하려고 시도하면 강제 종료 될 때까지 무시되어 코드의 다른 부분이 정상적으로 종료되지 않습니다. 디스크 또는 DB에 쓰는 동안 강제 종료하고 멋진 일이 일어날 것이라고 확신합니다.
Ajax
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.