Runnable의 run () 예외를 발생시키는 방법이 있습니까?


80

Runnable ) 을 구현하는 클래스의 run () 에서 호출하는 메서드 는 예외를 throw하도록 설계되었습니다.

그러나 Java 컴파일러는 그렇게 할 수 없으며 try / catch로 둘러싸라고 제안합니다.

문제는 그것을 try / catch로 둘러 싸서 특정 run ()을 쓸모 없게 만든다는 것 입니다 . 내가 그 예외를 throw합니다.

내가 지정하는 경우 throws에 대한 ) (실행 자체를, 컴파일러는 불평 Exception is not compatible with throws clause in Runnable.run().

보통 나는 run () 이 예외를 던지 도록하지 않아도 괜찮습니다 . 하지만 그 기능이 있어야하는 독특한 상황이 있습니다.

이 제한을 어떻게 해결합니까?


다른 답변 외에도 작업 진행 상황을 추적하기 위해 FutureTask 클래스를 사용할 수 있습니다.
JProgrammer

답변:


26

당신이 구현하는 클래스를 전달하려는 경우 RunnableThread프레임 워크, 당신은 그 프레임 워크의 규칙에 의해 놀이에이를, 그렇지 않으면 그 일을하는 것은 좋은 생각이 왜, 어니스트 프리드먼 - 힐의 답변을 참조하십시오.

그러나 run코드에서 직접 메서드 를 호출 하여 호출 코드에서 예외를 처리 할 수 있다는 직감이 있습니다 .

이 문제에 대한 답은 간단합니다. Runnable스레드 라이브러리의 인터페이스를 사용하지 말고 대신 확인 된 예외를 throw 할 수 있도록 수정 된 서명으로 자신의 인터페이스를 만듭니다.

public interface MyRunnable
{
    void myRun ( ) throws MyException;
}

이 인터페이스를 RunnableThread 프레임 워크에서 사용하기에 적합한 실제 (checked exception 처리) 로 변환하는 어댑터를 만들 수도 있습니다 .


이러한 간단한 해결책은 "상자 속"을 생각하지 않는 것입니다. 물론 a Runnable는 단순한 인터페이스 일 뿐이며 직접 만들 수 있습니다. 쓰레드 유스 케이스에는 유용하지 않지만, 다른 "실행 가능한"코드 덩어리를 전달하는 것은 완벽합니다.
Richard Le Mesurier

82

당신은을 사용할 수 있습니다 Callable에 제출, 대신 ExecutorService과 함께 결과를 기다리고 FutureTask.isDone()에 의해 반환 ExecutorService.submit().

isDone()true를 반환하면 전화 FutureTask.get(). 당신은 지금, 만약 Callable던져 않은는 Exception다음 FutureTask.get()을 던져 wiill Exception도 원래 예외는 사용하여 액세스 할 수 있습니다 Exception.getCause().


23

run()확인 된 예외를 던졌다 면 무엇이 그것을 잡을까요? run()호출을 호출하는 코드를 작성하지 않기 때문에 해당 호출을 핸들러에 넣을 수있는 방법이 없습니다.

run()메서드 에서 확인 된 예외를 포착 RuntimeException하고 그 자리에 체크 되지 않은 예외 (예 :)를 throw 할 수 있습니다 . 스택 추적으로 스레드를 종료합니다. 아마도 그것은 당신이 추구하는 것입니다.

대신 run()메서드가 어딘가에 오류를보고하도록하려면 run()메서드의 catch블록을 호출 할 콜백 메서드를 제공하면 됩니다. 이 메서드는 예외 개체를 어딘가에 저장할 수 있으며 관심있는 스레드는 해당 위치에서 개체를 찾을 수 있습니다.


2
첫 번째 부분은 좋은 주장이 아닙니다. " main()체크 된 예외를 던졌다 면 무엇이 그것을 잡을까요?" " run()확인되지 않은 예외를 던졌다 면 어떻게 잡을까요?"
기독교 Hujer

17

예, 메서드 에서 확인 된 예외 를 throw하는 run()방법이 있지만 너무 끔찍해서 공유하지 않을 것입니다.

대신 할 수있는 작업은 다음과 같습니다. 런타임 예외가 실행하는 것과 동일한 메커니즘을 사용합니다.

@Override
public void run() {
  try {
    /* Do your thing. */
    ...
  } catch (Exception ex) {
    Thread t = Thread.currentThread();
    t.getUncaughtExceptionHandler().uncaughtException(t, ex);
  }
}

다른 사람들이 언급했듯이 run()메서드가 실제로의 대상 Thread이면 관찰 할 수 없기 때문에 예외를 throw 할 필요가 없습니다. 예외를 던지는 것은 예외를 던지지 않는 것과 같은 효과가 있습니다 (없음).

그것이 아니라면 Thread대상, 사용하지 않습니다 Runnable. 예를 들어, 아마도 Callable더 적합합니다.


그러나 이것이 던질 때 프로세스가 충돌하는 원인이됩니까 ??
Dinesh

@DineshVG 아니요, JVM의 버그만 진정한 충돌을 일으킬 수 있습니다. 기본 예외 처리기는 예외를 인쇄합니다. 그 후 프로세스 종료를 보는 데 익숙하다면 해당 스레드가 실행중인 유일한 스레드 였고 종료 되었기 때문입니다.
erickson

나는 (안드로이드 계측 테스트 사례에서) 이것을 비누 호출에 사용하여 시도했는데, Soap 호출에서 400을 받으면 예외가 발생합니다. 이 SOAP 호출은 테스트 케이스를 시작하는 동안 스레드에서 호출됩니다. 이 스레드는 이것을 사용 t.getUncaughtExceptionHandler().uncaughtException(t, ex);하여 인스 트루먼 테이션 테스트 케이스에 던집니다. 이 한 줄을 추가하면 프로세스가 중단됩니다!. 이유를 모릅니다.
Dinesh

@DineshVG 그 환경에서 Thread.getDefaultUncaughtExceptionHandler()반환 null합니까? 그렇지 않은 경우 결과 유형은 무엇입니까? 를 호출하는 대신 uncaughtException()확인 된 예외를 a에 래핑 RuntimeException하고 throw하면 어떻게됩니까?
erickson

1
@DineshVG Android는이를 수행하는 기본 포착되지 않은 예외 처리기를 설정할 수 있습니다. 그것이 내가 Thread.getDefaultUncaughtExceptionHandler()반환 여부를 묻는 이유입니다 null. 그렇지 않은 경우 Android는보고 등을 제공하기 위해 기본값을 제공합니다.하지만 원하는 작업을 수행하도록 설정할 수 있습니다. 여기에 더 많은 정보가 있습니다.
erickson

6
@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {

    @Override
    default void run() throws RuntimeException {
        try {
            runThrows();
        }
        catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    void runThrows() throws E;

}

1

어떤 사람들은 당신이 규칙을 따라야한다고 당신을 설득하려고합니다. 하지만 순종 여부는 상황에 따라 결정해야합니다. 현실은 "규칙에 따라 플레이해야합니다"( "규칙에 따라 플레이해야합니다"가 아님). 규칙을 지키지 않으면 결과가 발생할 수 있습니다.

상황은의 상황 Runnable뿐만 아니라 Java 8에서도 매우 빈번하게 적용되며 검사 된 예외를 처리 할 가능성없이 기능 인터페이스가 도입 된 Streams 및 기타 장소의 컨텍스트에서도 매우 자주 발생합니다. 예를 들어, Consumer, Supplier, Function, BiFunction등 모두 체크 된 예외를 처리하는 시설없이 선언합니다.

그렇다면 상황과 옵션은 무엇입니까? 아래 텍스트에서는 Runnable예외를 선언하지 않거나 당면한 사용 사례에 비해 너무 제한적인 예외를 선언하는 기능 인터페이스를 나타냅니다.

  1. 당신은 Runnable어딘가에 선언 했고 Runnable다른 것으로 대체 할 수 있습니다.
    1. 교체 고려 Runnable와 함께 Callable<Void>. 기본적으로 똑같지 만 예외를 던질 수 있습니다. 보유하고 return null온화한 성가심 끝있다.
    2. 원하는 예외를 정확히 throw 할 수있는 Runnable사용자 지정으로 교체 하는 것을 고려하십시오 @FunctionalInterface.
  2. API를 사용했으며 대안을 사용할 수 있습니다. 당신이 사용할 수 있도록 예를 들어, 일부 자바 API는 오버로드 Callable<Void>대신 Runnable.
  3. API를 사용했으며 대안이 없습니다. 이 경우에도 여전히 옵션이 없습니다.
    1. 에서 예외를 래핑 할 수 있습니다 RuntimeException.
    2. 체크되지 않은 캐스트를 사용하여 예외를 RuntimeException으로 해킹 할 수 있습니다.

다음을 시도 할 수 있습니다. 약간의 해킹이지만 때로는 해킹이 필요합니다. 왜냐하면 예외를 확인해야하는지 여부를 확인해야하는지 여부는 유형에 따라 정의되지만 실제로는 상황에 따라 정의되어야하기 때문입니다.

@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
    @Override
    default void run() {
        try {
            tryRun();
        } catch (final Throwable t) {
            throwUnchecked(t);
        }
    }

    private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
        throw (E) t;
    }

    void tryRun() throws Throwable;
}

new RuntimeException(t)스택 추적이 더 짧기 때문에 이것을 선호 합니다.

이제 다음을 수행 할 수 있습니다.

executorService.submit((ThrowingRunnable) () -> {throw new Exception()});

면책 조항 : 이러한 방식으로 확인되지 않은 캐스트를 수행하는 기능은 제네릭 유형 정보가 컴파일 타임뿐만 아니라 런타임에도 처리 될 때 Java의 향후 버전에서 실제로 제거 될 수 있습니다.


0

귀하의 요구 사항이 의미가 없습니다. 발생한 예외에 대해 스레드의 호출자에게 알리려면 콜백 메커니즘을 통해이를 수행 할 수 있습니다. 이것은 핸들러 나 브로드 캐스트 또는 당신이 생각할 수있는 모든 것을 통할 수 있습니다.


0

리스너 패턴 이이 시나리오에 도움이 될 것이라고 생각합니다 . run()메서드 에서 예외가 발생하는 경우 try-catch 블록을 사용하고 catch에서 예외 이벤트 알림을 보냅니다. 그런 다음 알림 이벤트를 처리하십시오. 나는 이것이 더 깨끗한 접근이 될 것이라고 생각합니다. 이 SO 링크 는 그 방향에 대한 유용한 포인터를 제공합니다.


-1

가장 쉬운 방법은 RuntimeException클래스 대신 클래스 를 확장하는 고유 한 예외 객체를 정의하는 것 Exception입니다.


그리고 내 친애하는 선생님이 발생했을 때이 RuntimeException을 어떻게 잡을 수 있습니까?
Dormouse

그것만으로는 질문에 답할 수 없습니다.
Karl Richter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.