programing

스레드에서 예외를 포착하는 방법

goodsources 2022. 8. 3. 23:16
반응형

스레드에서 예외를 포착하는 방법

자바 메인 클래스가 있는데, 클래스에서 새 스레드를 시작하면 기본적으로 스레드가 사라질 때까지 기다립니다.어느 순간 스레드에서 런타임 예외를 발생시키지만 메인 클래스의 스레드에서 발생한 예외를 포착할 수 없습니다.

코드는 다음과 같습니다.

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

가장 가능성이 높은 것 같다.

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

그러나 하위 스레드에서 다른 예외를 처리해야 한다고 가정합니다.다음과 같은 Executor Service를 사용합니다.

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를 봐주세요.Unaught Exception 핸들러

Callable과 Future를 사용하여 동일한 결과를 얻는 것이 좋습니다.

Callable대신 레레대 instead instead instead instead instead instead instead instead instead instead instead instead instead라고 불러도 .Future#get()캘러블이 던진 예외는 모두 무시합니다.

되어 있습니다.RuntimeException의 " " " " "Exception그러나 어플리케이션이 예외의 다른 서브클래스를 슬로우할 수 있습니다.범용 캐치Exception에에 to in RuntimeException

스레딩 전면에서는 많은 것이 변경되었으므로 고급 Java API를 사용합니다.

멀티스레딩에는 다음과 같은 고급 java.util.concurrent API를 선호합니다.ExecutorService ★★★★★★★★★★★★★★★★★」ThreadPoolExecutor

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

Oracle 문서 페이지의 예:

오버라이드

protected void afterExecute(Runnable r,
                            Throwable t)

지정된 실행 가능 실행 완료 시 호출되는 메서드입니다.이 메서드는 태스크를 실행한 스레드에 의해 호출됩니다.null이 아닌 경우 throwable은 수집되지 않은 런타임입니다.실행이 갑자기 종료된 예외 또는 오류입니다.

코드 예:

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();

위의 코드 위에 다음과 같이 컨스트럭터를 1개 추가했습니다.

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

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

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

나도 같은 문제에 직면했어...(어나니머스 오브젝트가 아닌 구현의 경우에만)... 클래스 레벨 예외 오브젝트를 null로 선언할 수 있습니다.그런 다음 run method의 catch block 내에서 초기화... run method에 오류가 있으면 이 변수는 null이 되지 않습니다.그러면 이 특정 변수에 대해 null 체크를 할 수 있습니다.null이 아닌 경우 스레드 실행 내에 예외가 있습니다.

class TestClass implements Runnable{
    private Exception ex;

        @Override
        public void run() {
            try{
                //business code
               }catch(Exception e){
                   ex=e;
               }
          }

      public void checkForException() throws Exception {
            if (ex!= null) {
                throw ex;
            }
        }
}     

join() 후에 check For Exception()을 호출한다.

또한 Java 8에서는 Dan Cruz의 답변을 다음과 같이 쓸 수 있습니다.

Thread t = new Thread(()->{
            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((th, ex)-> log(String.format("Exception in thread %d id: %s", th.getId(), ex)));
t.start();

또한 Atomic Reference는 메인 스레드에 오류를 전달하기 위한 솔루션입니다.Dan Cruz와 같은 접근법입니다.

AtomicReference<Throwable> errorReference = new AtomicReference<>();

    Thread thread = new Thread() {
        public void run() {
            throw new RuntimeException("TEST EXCEPTION");

        }
    };
    thread.setUncaughtExceptionHandler((th, ex) -> {
        errorReference.set(ex);
    });
    thread.start();
    thread.join();
    Throwable newThreadError= errorReference.get();
    if (newThreadError!= null) {
        throw newThreadError;
    }  

유일한 변경사항은 휘발성 변수를 작성하는 대신 뒤에서 동일한 작업을 수행한 Atomic Reference를 사용할 수 있다는 것입니다.

setDefaultUncaughtExceptionHandler() 및 스레드 클래스의 동일한 메서드를 사용하고 있습니까?API에서: "기본 미취득 예외 핸들러를 설정함으로써 응용 프로그램은 시스템이 제공하는 모든 "기본" 동작을 이미 받아들이는 스레드에 대해 미취득 예외 처리 방법(특정 디바이스 또는 파일에 로깅 등)을 변경할 수 있습니다."

당신은 당신의 문제에 대한 답을 거기서 찾을 수 있을 것이다.행운을 빌어요! :-)

스레드를 구현하는 경우.스레드를 시작하는 클래스의 UnaughtExceptionHandler에서 예외를 설정하고 다시 던질 수 있습니다.

public final class ThreadStarter implements Thread.UncaughtExceptionHandler{

private volatile Throwable initException;

    public void doSomeInit(){
        Thread t = new Thread(){
            @Override
            public void run() {
              throw new RuntimeException("UNCAUGHT");
            }
        };
        t.setUncaughtExceptionHandler(this);

        t.start();
        t.join();

        if (initException != null){
            throw new RuntimeException(initException);
        }

    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        initException =  e;
    }    

}

이것에 의해, 다음의 출력이 발생합니다.

Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: UNCAUGHT
    at com.gs.gss.ccsp.enrichments.ThreadStarter.doSomeInit(ThreadStarter.java:24)
    at com.gs.gss.ccsp.enrichments.ThreadStarter.main(ThreadStarter.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.RuntimeException: UNCAUGHT
    at com.gs.gss.ccsp.enrichments.ThreadStarter$1.run(ThreadStarter.java:15)

스레드에서의 예외 처리 : 기본적으로 run() 메서드는 예외를 슬로우하지 않으므로 실행 메서드 내에서 체크된 예외는 모두 거기서만 포착하여 처리해야 합니다.실행 시 예외에 대해서는 UnaughtExceptionHandler를 사용할 수 있습니다.UnaughtExceptionHandler는 스레드 실행 메서드에서 예외를 처리하기 위해 Java에서 제공하는 인터페이스입니다.따라서 이 인터페이스를 구현하고 setUncaughtExceptionHandler() 메서드를 사용하여 구현 클래스를 스레드 오브젝트로 되돌릴 수 있습니다.단, 이 핸들러는 트레드에서 start()를 호출하기 전에 설정해야 합니다.

unchaughtExceptionHandler를 설정하지 않으면 스레드 스레드 그룹이 핸들러 역할을 합니다.

 public class FirstThread extends Thread {

int count = 0;

@Override
public void run() {
    while (true) {
        System.out.println("FirstThread doing something urgent, count : "
                + (count++));
        throw new RuntimeException();
    }

}

public static void main(String[] args) {
    FirstThread t1 = new FirstThread();
    t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
        public void uncaughtException(Thread t, Throwable e) {
            System.out.printf("Exception thrown by %s with id : %d",
                    t.getName(), t.getId());
            System.out.println("\n"+e.getClass());
        }
    });
    t1.start();
}
}

자세한 설명은 http://coder2design.com/thread-creation/#에서 확인할 수 있습니다.

연장하는 것은 거의 항상 잘못된 것이다.Thread이 점은 아무리 강조해도 지나치지 않습니다.

멀티스레딩 규칙 1: 확장Thread틀렸습니다.*

실장하는 경우Runnable대신 예상되는 행동을 보게 될 것입니다.

public class Test implements Runnable {

  public static void main(String[] args) {
    Test t = new Test();
    try {
      new Thread(t).start();
    } 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");

        Thread.sleep(2000);

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

    }
  }
}

생산하다

Main stoped
** Started
** RuntimeException from threadException in thread "Thread-0" java.lang.RuntimeException: exception from thread
    at Test.run(Test.java:23)
    at java.lang.Thread.run(Thread.java:619)

* 어플리케이션의 스레드 사용 방법을 변경하고 싶지 않은 경우(99.9%의 경우) 이외에는.0.1%의 경우 규칙 1을 참조하십시오.

RxJava를 사용한 솔루션:

@Test(expectedExceptions = TestException.class)
public void testGetNonexistentEntry() throws Exception
{
    // using this to work around the limitation where the errors in onError (in subscribe method)
    // cannot be thrown out to the main thread
    AtomicReference<Exception> ex = new AtomicReference<>();
    URI id = getRandomUri();
    canonicalMedia.setId(id);

    client.get(id.toString())
        .subscribe(
            m ->
                fail("Should not be successful"),
            e ->
                ex.set(new TestException()));

    for(int i = 0; i < 5; ++i)
    {
        if(ex.get() != null)
            throw ex.get();
        else
            Thread.sleep(1000);
    }
    Assert.fail("Cannot find the exception to throw.");
}

모든 스레드 실행을 중지하고 예외에서 스레드 중 하나가 중지되었을 때 모든 스레드를 다시 실행해야 하는 경우:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {

     // could be any function
     getStockHistory();

}


public void getStockHistory() {

     // fill a list of symbol to be scrapped
     List<String> symbolListNYSE = stockEntityRepository
     .findByExchangeShortNameOnlySymbol(ContextRefreshExecutor.NYSE);


    storeSymbolList(symbolListNYSE, ContextRefreshExecutor.NYSE);

}


private void storeSymbolList(List<String> symbolList, String exchange) {

    int total = symbolList.size();

    // I create a list of Thread 
    List<Thread> listThread = new ArrayList<Thread>();

    // For each 1000 element of my scrapping ticker list I create a new Thread
    for (int i = 0; i <= total; i += 1000) {
        int l = i;

        Thread t1 = new Thread() {

            public void run() {

                // just a service that store in DB my ticker list
                storingService.getAndStoreStockPrice(symbolList, l, 1000, 
                MULTIPLE_STOCK_FILL, exchange);

            }

        };

    Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread thread, Throwable exception) {

                // stop thread if still running
                thread.interrupt();

                // go over every thread running and stop every one of them
                listThread.stream().forEach(tread -> tread.interrupt());

                // relaunch all the Thread via the main function
                getStockHistory();
            }
        };

        t1.start();
        t1.setUncaughtExceptionHandler(h);

        listThread.add(t1);

    }

}

정리하면:

여러 스레드를 작성하는 주요 함수가 있으며, 각 스레드에는 스레드 내의 예외에 의해 트리거되는 UnchaughtExceptionHandler가 있습니다.모든 스레드를 목록에 추가합니다.UnaughtExceptionHandler가 트리거된 경우 목록을 루프하고 모든 스레드를 중지하고 모든 스레드의 주요 기능 재생을 다시 시작합니다.

말이 안 되니까 이러면 안 돼요.네가 전화하지 않았다면t.join()메인 스레드가 코드의 어디에나 있을 수 있습니다.t스레드가 예외를 발생시킵니다.

언급URL : https://stackoverflow.com/questions/6546193/how-to-catch-an-exception-from-a-thread

반응형