티스토리 뷰

Java

UncaughtExceptionHandler 인터페이스

LichKing 2021. 1. 25. 18:39

자바에는 UncaughtExceptionHandler 라는 인터페이스가 있다. 생소한 인터페이스일텐데 jdk 1.5 에 추가된 인터페이스이다. Thread 클래스의 내부 인터페이스로 선언되어있는데, 위치만 봐도 알 수 있듯이 쓰레드에 관련한 인터페이스이다. 쓰레드가 동작할때 내부에서 발생한 예외를 처리하지 않을 경우 UncaughtExceptionHandler 인터페이스의 구현체에서 예외를 처리할 수 있다.

 

@FunctionalInterface                                                   
public interface UncaughtExceptionHandler {                            
    /**                                                                
     * Method invoked when the given thread terminates due to the      
     * given uncaught exception.                                       
     * <p>Any exception thrown by this method will be ignored by the   
     * Java Virtual Machine.                                           
     * @param t the thread                                             
     * @param e the exception                                          
     */                                                                
    void uncaughtException(Thread t, Throwable e);                     
}                                                                      

함수형 인터페이스로 매우 단순한 형태이다. 예외를 처리할 쓰레드와, 발생한 예외를 파라미터로 받는다. 어떤식으로 구성할 수 있는지 확인해보자.

 

public static void main(String[] args) {                                       
    Thread.UncaughtExceptionHandler handler = (t, e) -> {                      
        System.out.println("thread :: " + t.getName() + " exception :: " + e); 
    };                                                                         
                                                                               
    Thread t = new Thread(() -> {                                              
        for(int i = 0; i < 100; i++) {                                         
            System.out.println("hello thread");                                
        }                                                                      
        throw new RuntimeException();                                          
    });                                                                        
    t.setUncaughtExceptionHandler(handler);                                    
    t.start();                                                                 
}                                                                              

람다를 이용해 UncaughtExceptionHandler 를 구현했다. 그리고 추가 쓰레드를 하나 생성하고, 해당 쓰레드에 핸들러를 등록해줬다.

Runnable 람다를 보면 for 문 바깥에 RuntimeException 을 일으키게되어있는데, 우리가 구현한 인터페이스는 말 그대로 예외 핸들러이기때문에 쓰레드 내에서 예외가 발생하지 않을 경우 해당 구현체는 작동하지 않는다. 구현체의 동작을 확인하기 위해 강제로 예외를 일으켰다.

 

코드를 실행해보면 잘 돌아가는걸 확인 할 수 있다. 하지만 Thread 객체를 직접 생성하는건 그다지 권장하는 방식이 아니다. Executor 를 이용하는 코드를 확인해보자.

 

public static void main(String[] args) {                                                      
    Thread.UncaughtExceptionHandler handler = (t, e) -> {                                     
        System.out.println("thread :: " + t.getName() + " exception :: " + e);                
    };                                                                                        
                                                                                              
    ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() {     
        @Override                                                                             
        public Thread newThread(Runnable r) {                                                 
            Thread thread = Executors.defaultThreadFactory().newThread(r);                    
            thread.setUncaughtExceptionHandler(handler);                                      
                                                                                              
            return thread;                                                                    
        }                                                                                     
    });                                                                                       
                                                                                              
    executorService.execute(() -> {                                                           
        for(int i = 0; i < 100; i++) {                                                        
            System.out.println("hello thread");                                               
        }                                                                                     
        throw new RuntimeException();                                                         
    });                                                                                       
    executorService.shutdown();                                                               
}                                                                                             

Executors 에 정의된 팩토리 메서드는 ThreadFactory 라는 인터페이스를 선택 인자로 받고있다. ThreadFactory 자체도 람다로 구현할 수 있지만 지금은 타입을 좀 더 확인하기 쉽게 익명 클래스로 작성했다. ThreadFactory 인터페이스가 Thread 를 리턴해야 하기 때문에 해당 메서드 내에서 핸들러를 등록할 수 있다. 나는 defaultThreadFactory 를 이용했지만 저 코드에서 new Thread 를 해도 무방하다.

 

사실 앞으로도 쓸 일이 많을 것 같지는 않다. 하지만 필요할때 요긴하게 써먹을 수 있도록 남겨놓는다.

댓글
댓글쓰기 폼