OS

OS 스레드의 예외 처리

데일리코딩 2024. 9. 3. 22:23

* 해당 포스팅은 김영한님의 자바 고급편 스레드 강의 영상을 보고 정리한 포스팅입니다.

 

안녕하세요 지난 포스팅에 이어서 오늘은 스레드의 예외 처리에 대해서 이야기 해보고자 합니다.

 

우선 스레드에서 예외 처리를 진행하기 전에 자바의 예외에 대해서 약간의 학습을 진행한다면

스레드의 예외처리를 어떻게 하면 깔끔하게 처리할 수 있을지 고민할 수 있을거 같아

 

자바의 예외 객체부터 살펴보는 시간을 가져보도록 하겠습니다.


자바의 예외 계층도

자바의 전체 예외 계층도 입니다. 전체라기 보다는 상위 계층도라고 할 수 있겠습니다

에러와 예외를 간단하게 정의부터 시작해보겠습니다.

더보기

1.  프로그램의 오류

- 컴파일 오류: 개발자가 IDE 에서 개발을 할 때 잘못된 문법을 사용할때 컴파일에서 미리 개발자에게 에러를 알리는 것입니다.

- 런타임 에러: 실행 할 때 발생하는 에러 입니다.

- 논리적 에러: 작성 의도와 다르게 동작하는것을 의미합니다.

 

위 그림을 살펴보면 Trowable 객체밑에 Exception (예외), Error(에러)가 존재하는데 이 둘의 차이는 뭘까요?

에러는 프로그램 코드에 의해서 수습할 수 없는 심각한 오류입니다.

예외의 경우에는 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류라고 생각할 수 있겠습니다.

하지만 이때 예외 밑에 checked 예외와 Unchcked 예외가 있는데 이 둘의 큰 차이점이 존재합니다.

 

더보기

2. 체크 예외와 언체크 예외의 차이

체크예외의 경우에는 RuntimeException을 제외한 Exception 클래스들을 컴파일 시 try - catch 문을 반드시 잡아주거나

throw (던져야한다) 그렇지 않으면 컴파일 단계에서 오류가 발생합니다.

 

반면 언체크 예외는 개발자가 잡아서 처리하거나 외부로 던지지 않아도 됩니다. 개발자가 예외 처리를 생략한다면 언체크 예외는 자동으로 외부로 던져집니다. 


그래서 결론적으로 어떤 예외를 사용해야하는가?

결론적으로 2가지 선택이 있는데

1. 기본적으로 런타임 예외를 사용하기

2. 체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용합니다.
해당 예외를 반드시 잡아서 처리할 때만 체크 예외를 사용해야합니다.

예) 계좌 이체 실패 예외, 결제시 포인트 부족 예외, 로그인 ID, PW 불일치 예외 등등..

하지만 이 마저도 매뉴얼만 잘 만들어놓는다면 런타임 예외로 처리하는 경우가 많습니다


드디어 스레드 예외 처리

우리가 Runnable 인터페이스의 run() 메서드를 구현할 때 InterruptedException 체크 예외를 밖으로 던질 수가 없습니다

그 이유는 Runnable 인터페이스를 확인하면 알 수 있는데

 

public interface Runnable {
void run();
}

이처럼 run() 메서드는 throw를 하지 않아 이를 구현하는 구현 객체 메소드에서도 예외를 던질 수가 없는것입니다

자바에서는 부모 메서드가 체크 예외를 던지지 않는 경우, 재정의된 자식 메서드도 체크 예외를 던질 수 없습니다. 

자식 메서드는 부모 메서드가 던질 수 있는 체크 예외의 하위 타입만 던질 수 있는데요

 

자바에서 이렇게 설계한 이유는 예외처리의 일관성을 지키고 예상하지 못한 런타임 오류를 막아주기 위해서 

자식 메서드는 부모 메서드 보다 범위가 넓은 예외를 던질 수가 없습니다.

 

그래서 Runnable 인터페이스를 구현한 객체 메서드 run() 메서드를 호출하고 이때 예외 처리를 꼭 해줘야 하는 상황이 벌어집니다.

void run() {
    try {
    	Thread.sleep(3000);
    } catch (InterruptedException e) {
    	throw new RuntimeException(e);
    }
}

// Runnable 인터페이스가 예외를 던지지 않아 무조건 try - catch 를 사용해야하는 상황

 

하지만 이 코드를 간결하게 하는 방법이 있는데요 

우리가 위에서 살펴보았던 언체크 예외를 활용하는 것인데요

언체크 예외를 사용한다면 try - catch 혹은 throw 해줄 필요 없이 생략이 가능해집니다. 

만약 예외가 발생하다면 자동으로 throw 해서 호출하는 쪽에서 받을 것이구요

 

아래 코드를 통해 예시를 살펴보세요.

package util;
import static util.MyLogger.log;

public abstract class ThreadUtils {
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            // 특정 코드에서 예외를 잡고 언체크 예외로 처리하기
            log("인터럽트 발생, " + e.getMessage());
            throw new RuntimeException(e);
        }
	}
}

 

 

이렇게 예외를 처리해줘야 하는 메서드 내부에서 예외를 언체크 예외 처리를 통해 깔끔하고 우아하게 스레드 run() 메서드를 깔끔하게 처리

할 수 있게 되었습니다.

 

실제로는 개발자가 직접 Runnable 을 구현해서 스레드를 관리하거나 하진 않습니다 

이 인터페이스 기반으로 이 문제를 해결하기 위해서 한층 업그레드이 된 기능들이 있는데요 나중에 해당 기능들에 소개 해드리겠습니다.

하지만 이번 포스팅을 통해서 우리는 자바의 예외 계층 부터 어떤 예외를 사용해야 하고 어떤 상황에서 어떻게 예외 처리할지 

그리고 스레드에서 예외처리를 통해서 언체크 예외 활용까지 알아보았습니다.

 

앞으로 꾸준히 스레드 관련 포스팅을 올릴 예정이니 많은 관심부탁드리겠습니다

감사합니다!