Hello, Freakin world!

[Java] CompletableFuture를 이용해 비동기 예외 처리하기 본문

프로그래밍 언어/Java

[Java] CompletableFuture를 이용해 비동기 예외 처리하기

johnna_endure 2020. 3. 16. 07:46

비동기 예외 처리?

상황을 자세히 살펴보자. 

스레드 A,B가 있다고 하자. A는 호출자 스레드이고 A스레드에서 어떤 작업을 B로 위임했다.

B 작업의 결과를 참조하기 위해 우리는 자바에서 지원하는 동시성 api를 사용했고

그 결과는 Future를 통해 접근할 수 있다고 하자.

예제 코드

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadA {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Task task = new Task();
        Future future = executorService.submit(task::doSomething);
        future.get();

        System.out.println("main 종료");
    }

}
class Task {
    public void doSomething() {
        throw new RuntimeException("");
    }
}

다른 스레드(ThreadB)에 task를 위임했을 때, 예외를 던지도록 해보자.

이 경우 예외는 호출자 스레드까지 전파되는데, 이는 get 메서드 명세를 보면 알 수 있다.

 

1. java.util.concurrent.CancellationException – if the computation was cancelled
2. java.util.concurrent.ExecutionException – if the computation threw an exception
3. InterruptedException – if the current thread was interrupted while waiting

 

위의 예제에서는 RuntimeException이 2번에 해당되기 때문에 ExecutionException 를 던진다.

 

하지만 만약 예외없이 무한정 대기해야되는 상황이 오면 어떨까?

이 경우 오버로드된 get 메서드를 이용해 타임아웃을 지정해 줄 수 있다. 시간이 다됐을 경우 TimeoutException 을 던지게 되고 이를 처리하면 된다.

하지만 이 경우 ThreadB에서 무슨 일이 일어난건지는 알 수가 없다. 그냥 단순히 시간초과에 의한 TimeoutException를 받을 뿐이다.  여기서 CompletableFuture를 사용하면 ThreadB 내부의 예외를 호출자 스레드에 전달할 수 있다.

 

예제 코드

public class ThreadA {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Task task = new Task();
        Future<String> future = task.doSomething();
        System.out.println(future.get());

        System.out.println("main 종료");
    }

}
class Task {
    public Future<String> doSomething() {
        CompletableFuture<String> requestFuture = new CompletableFuture<>();
        new Thread(() -> {
            try{
                requestFuture.complete("hello, world!");
            }catch (Exception ex) {
                requestFuture.completeExceptionally(ex); // 도중에 문제가 발생하면 에러를 포함시켜 Future 종료.
            }
        }).start();
        return requestFuture;
    }
}

completeExceptionally 메서드를 통해 내부의 예외를 future 객체에 전달하는 것을 볼 수 있다.

 

 

위의 doSomething는 팩토리 메서드에 의해 다음으로 대체될 수 있다.

    public Future<String> doSomething() {
        return CompletableFuture.supplyAsync(() -> "Hello, world!");
    }

 

참고자료 

자바 8 in action - 라울-게이브리얼 우르마, 마리오 푸스코, 앨런 마이크로프트 지음

Comments