[java] 스레드에서 예외를 잡는 방법

Java 메인 클래스가 있습니다. 클래스에서 새 스레드를 시작합니다. 메인에서 스레드가 죽을 때까지 기다립니다. 언젠가 스레드에서 런타임 예외가 발생하지만 주 클래스의 스레드에서 발생한 예외를 잡을 수 없습니다.

코드는 다음과 같습니다.

public class Test extends Thread
{
  public static void main(String[] args) throws InterruptedException
  {
    Test t = new Test();

    try
    {
      t.start();
      t.join();
    }
    catch(RuntimeException e)
    {
      System.out.println("** RuntimeException from main");
    }

    System.out.println("Main stoped");
  }

  @Override
  public void run()
  {
    try
    {
      while(true)
      {
        System.out.println("** Started");

        sleep(2000);

        throw new RuntimeException("exception from thread");
      }
    }
    catch (RuntimeException e)
    {
      System.out.println("** RuntimeException from thread");

      throw e;
    } 
    catch (InterruptedException e)
    {

    }
  }
}

왜 그 이유를 아는 사람이 있습니까?



답변

를 사용하십시오 Thread.UncaughtExceptionHandler.

Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread th, Throwable ex) {
        System.out.println("Uncaught exception: " + ex);
    }
};
Thread t = new Thread() {
    @Override
    public void run() {
        System.out.println("Sleeping ...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("Interrupted.");
        }
        System.out.println("Throwing exception ...");
        throw new RuntimeException();
    }
};
t.setUncaughtExceptionHandler(h);
t.start();


답변

예외는 스레드에 국한되며 메인 스레드는 실제로 run메소드를 보지 않기 때문 입니다. 스레딩의 작동 방식에 대해 자세히 읽어 보지만 요약하면 start주 스레드와 전혀 관련이없는 다른 스레드 를 시작 하라는 요청 이 있습니다. 호출은 join단순히 완료되기를 기다립니다. 스레드에서 발생하고 포착되지 않은 예외는 종료되므로 join주 스레드에서 반환되지만 예외 자체는 손실됩니다.

이러한 잡히지 않은 예외를 알고 싶다면 다음을 시도하십시오.

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("Caught " + e);
    }
});

포착되지 않은 예외 처리에 대한 자세한 내용은 여기를 참조하십시오 .


답변

이것은 예외 발생 여부에 따라 스레드의 상태 전이를 설명합니다.

스레드와 예외 처리

출처 : http://www-public.imtbs-tsp.eu/~gibson/Teaching/CSC7322/L8-ExceptionsAndThreads.pdf


답변

가능성이 높습니다.

  • 한 스레드에서 다른 스레드로 예외를 전달할 필요는 없습니다.
  • 예외를 처리하려면 스레드에서 예외를 처리하십시오.
  • 이 예제에서 기본 스레드는 백그라운드 스레드에서 기다릴 필요가 없습니다. 실제로 백그라운드 스레드가 전혀 필요하지 않습니다.

그러나 자식 스레드에서 예외를 처리해야한다고 가정 해보십시오. 다음과 같이 ExecutorService를 사용합니다.

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Void> future = executor.submit(new Callable<Void>() {
    @Override
    public Void call() throws Exception {
        System.out.println("** Started");
        Thread.sleep(2000);
        throw new IllegalStateException("exception from thread");
    }
});
try {
    future.get(); // raises ExecutionException for any uncaught exception in child
} catch (ExecutionException e) {
    System.out.println("** RuntimeException from thread ");
    e.getCause().printStackTrace(System.out);
}
executor.shutdown();
System.out.println("** Main stopped");

인쇄물

** Started
** RuntimeException from thread
java.lang.IllegalStateException: exception from thread
    at Main$1.call(Main.java:11)
    at Main$1.call(Main.java:6)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
** Main stopped


답변

Thread.UncaughtExceptionHandler를 살펴보십시오.

더 나은 (대안적인) 방법은 CallableFuture 를 사용 하여 동일한 결과를 얻는 것입니다 …


답변

사용하여 Callable다음 호출 할 수 있습니다, 대신 스레드의 Future#get()Callable를 던진 것을 어떤 예외가 발생한다.


답변

현재, 당신은 RuntimeException하위 클래스의 잡기 만하고 있습니다 Exception. 그러나 응용 프로그램은 Exception의 다른 하위 클래스를 던질 수 있습니다 . Exception이외에도 일반 잡기RuntimeException

스레딩 프론트에서 많은 것들이 변경되었으므로 고급 자바 API를 사용하십시오.

사전 java.util.concurrent에서의 선호 API를 같은 멀티 스레딩을 위해 ExecutorService또는 ThreadPoolExecutor.

예외를 처리하도록 ThreadPoolExecutor 를 사용자 정의 할 수 있습니다 .

오라클 문서 페이지의 예 :

우세하다

protected void afterExecute(Runnable r,
                            Throwable t)

지정된 Runnable의 실행이 완료되면 호출 된 메소드입니다. 이 메소드는 태스크를 실행 한 스레드에 의해 호출됩니다. 널이 아닌 경우 Throwable은 catch되지 않은 RuntimeException 또는 Error로 인해 실행이 갑자기 종료됩니다.

예제 코드 :

class ExtendedExecutor extends ThreadPoolExecutor {
   // ...
   protected void afterExecute(Runnable r, Throwable t) {
     super.afterExecute(r, t);
     if (t == null && r instanceof Future<?>) {
       try {
         Object result = ((Future<?>) r).get();
       } catch (CancellationException ce) {
           t = ce;
       } catch (ExecutionException ee) {
           t = ee.getCause();
       } catch (InterruptedException ie) {
           Thread.currentThread().interrupt(); // ignore/reset
       }
     }
     if (t != null)
       System.out.println(t);
   }
 }

용법:

ExtendedExecutor service = new ExtendedExecutor();

위 코드 위에 생성자를 하나 추가했습니다.

 public ExtendedExecutor() {
       super(1,5,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
   }

스레드 수에 대한 요구 사항에 맞게이 생성자를 변경할 수 있습니다.

ExtendedExecutor service = new ExtendedExecutor();
service.submit(<your Callable or Runnable implementation>);