멀티스레드와 동시성

Ch03. 스레드 제어와 생명 주기 - 체크 예외 재정의

webmaster 2024. 8. 2. 13:54
728x90

Runnable 인터페이스의 run() 메서드를 구현할 때 "InterruptedException" 체크 예외를 밖으로 던질 수 없는 이유를 알아보자.

Runnable 인터페이스

 public interface Runnable {
     void run();
}

자바에서 메서드를 재정의 할 때, 재정의 메서드가 지켜야 할 예외와 관련된 규칙이 있다.

  • 체크 예외
    • 부모 메서드가 체크 예외를 던지지 않는 경우, 재정의된 자식 메서드도 체크 예외를 던질 수 없다.
    • 자식 메서드는 부모 메서드가 던질 수 있는 체크 예외의 하위 타입만 던질 수 있다.
  • 언체크(런타임) 예외
    • 예외 처리를 강제하지 않으므로 상관없이 던질 수 있다.

Runnable 인터페이스의 run() 메서드는 아무런 체크 예외를 던지지 않는다. 따라서 Runnable 인터페이스의

run() 메서드를 재정의 하는 곳에서는 체크 예외를 밖으로 던질 수 없다.

잘못된 코드 예시

잘못된 코드 예시
  • main()은 체크 예외를 밖으로 던질 수 있다.
  • run()은 체크 예외를 밖으로 던질 수 없다.

예를 들어 다음 코드의 `InterruptedException` 도 체크 예외이므로 던질 수 없다. 컴파일 오류가 발생한다.

static class MyRunnable implements Runnable {
     public void run() throws InterruptedException {
         Thread.sleep(3000);
     }
}

 

체크 예외 제약 사항을 둔 이유

부모 클래스의 메서드를 호출하는 클라이언트 코드는 부모 메서드가 던지는 특정 예외만을 처리하도록 작성된다. 자식 클래스가 더 넓은 범위의 예외를 던지면 해당 코드는 모든 예외를 제대로 처리하지 못할 수 있다. 이는 예외 처리의 일관성을 해치고, 예상하지 못한 런타임 오류를 초래할 수 있다.

실제 동작하는 코드는 X

class Parent {
     void method() throws InterruptedException {
    	// ...
    } 
}
class Child extends Parent {
     @Override
     void method() throws Exception {
         // ...
	}
}
public class Test {
     public static void main(String[] args) {
         Parent p = new Child();
         try {
             p.method();
         } catch (InterruptedException e) {
			// InterruptedException 처리 
         }
	}
}
  • 자바 컴파일러는 "Parent p"의 method()를 호출한 것으로 인지한다.
  • "Parent p"는 "InterruptedException" 를 반환하는데, 그 자식이 전혀 다른 예외를 반환한다면 클라이언트는 해당 예외를 잡을 수 없다. 이것은 확실하게 모든 예외를 체크하는 체크 예외의 규약에 맞지 않는다.
  • 따라서 자바에서 체크 예외의 메서드 재정의는 다음과 같은 규칙을 가진다.

체크 예외 재정의 규칙
자식 클래스에 재정의된 메서드는 부모 메서드가 던질 수 있는 체크 예외의 하위 타입만을 던질 수 있다.
원래 메서드가 체크 예외를 던지지 않는 경우, 재정의된 메서드도 체크 예외를 던질 수 없다.

 

안전한 예외 처리

체크 예외를 run() 메서드에서 던질 수 없도록 강제함으로써, 개발자는 반드시 체크 예외를 try-catch 블록 내에서 처리하게 된다.

이는 예외 발생 시 예외가 적절히 처리되지 않아서 프로그램이 비정상 종료되는 상황을 방지할 수 있다. 특히 멀티스레딩 환경에서는 예외 처리를 강제함으로써 스레드의 안정성과 일관성을 유지할 수 있다.

체크 예외를 강제하는 이런 부분들은 자바 초창기 기조이고, 최근에는 체크 예외보다는 언체크(런타임) 예외를 선호한다.

 

Sleep 유틸리티 작성하기

기존 코드

void run() {
     try {
         Thread.sleep(3000);
     } catch (InterruptedException e) {
         throw new RuntimeException(e);
     }
}
  • 매번 try-catch로 감싸는 것이 매우 불편하다.

ThreadUtils.sleep()

Thread 유틸리티

  • Sleep 함수에서 "RuntimeException"을 발생시키도록 변경하여 더 이상 sleep 함수 호출 시, Try-catch를 사용하지 않도록 한다.
728x90