PS, 언어 공부/JAVA

[Java] CompletableFuture의 개념과 동작원리, Thread, Future와의 비교

Emil :) 2024. 6. 2. 21:44
728x90
반응형

서론


모 회사에서 라이브 코딩테스트를 진행하며 스레드(혹은 비동기 태스크)를 활용한 비동기 프로그램 코딩을 진행했다.

검색 허용이 되서, 내가 알고있던 Thread를 쓸까 하다가 너무 기본적인 예제같은 느낌이 들어서 더 좋은 방법이 있나 찾아봤고, CompletableFuture 라는것에 대해 알게되었다.

기존 Thread랑 무슨 차이가 있는지, CompletableFuture는 뭔지 알아보도록 하자.

참고


https://stackoverflow.com/questions/49865210/thread-vs-completablefuture

 

Thread vs CompletableFuture

What is the advantage of passing code directly to thread vs using CompletableFuture instead? Thread thread = new Thread(() -> {do something}); thread.start(); VS CompletableFuture<Void>...

stackoverflow.com

https://medium.com/@afzaalshoukat/a-guide-to-multithreading-in-java-threads-future-and-completablefuture-a5bbe90a11a4

 

A Guide to Multithreading in Java: Threads, Future, and CompletableFuture

Introduction

medium.com

본론


CompletableFuture의 개념

자바 8부터 도입된 개념으로, 기존에 자바 5에서 등장한 Future의 성능과 안정성을 향상시킨 버전이다.
기존 Future는 우리가 알고있는 비동기 계산에 용이하나, 이 계산들을 결합하거나 오류를 처리할 방법이 없었기 때문에, 이러한 단점을 보완해서 나온 것이다.

실제로 코드를 뜯어보면, Future와 CompletationStage의 구현체인 것을 확인할 수 있다.
CompletionStage는 이건 꼭 완료될거야! 라고 선언하는 일종의 약속이라고 보면 된다.
해당 인터페이스를 통해 완료 후 콜백 등과 같은 작업을 할 수 있게 되었다고 한다. ex) 몇 초 이내에 응답값이 안오면 기본값을 반환한다.
Optional.map이나 Stream.map과 비슷하다.

public interface CompletionStage<T> {

// Returns a new CompletionStage that, when this stage completes normally, is executed with this stage's result as the argument to the supplied function.
// This method is analogous to Optional. map and Stream. map.

// 이 단계가 정상적으로 완료되면 이 단계의 결과를 제공된 함수에 대한 인수로 사용하여 실행되는 새 CompletionStage를 반환합니다.
// 이 방법은 Optional.map, Stream.map 과 유사합니다.
}

이러한 Future와 CompltationStage의 구현체인 것으로 미루어보아 디테일한 작업에 용이한 클래스라는 것을 유추해볼 수 있다.

작동원리는 다음과 같다. 코드를 뜯어보자

A CompletableFuture may have dependent completion actions, collected in a linked stack. It atomically completes by CASing a result field, and then pops off and runs those actions. This applies across normal vs exceptional outcomes, sync vs async actions, binary triggers, and various forms of completions.

CompletableFuture에는 연결된 스택에 수집된 종속 완료 작업이 있을 수 있습니다. 결과 필드를 CASing하여 원자적으로 완료된 다음 해당 작업이 튀어 나와 실행됩니다. 이는 정상 및 예외 결과, 동기화 및 비동기 작업, 바이너리 트리거 및 다양한 형태의 완료에 적용됩니다.

Linked stack에 작업들을 쌓아두고, 작업에 대한 결과를 CAS 알고리즘을 통해 원자적으로 작업이 처리가 되는구나! 라고 이해하면 편하다. CAS 알고리즘은 본 포스팅에선 다소 지엽적인 내용이니 넘어가도록 하자.

기능 소개

앞서 말한 것처럼, 기존 Future에서 제공하는 비동기 처리 이외에도 결합과 완료가 명확한 작업에 대한 콜백 처리를 해줄 수 있다.
몇가지 예시로 확인해보자.

비동기 작업 생성

대표적으로 supplyAsync를 통해 비동기 작업 공급자(supplier)를 생성할 수 있다.

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // 비동기 작업
    return 42;
});

 

후속 작업 체이닝

thenReturn, thenAccept, thenRun 등의 메서드를 통해 비동기 작업의 결과를 처리하는 후속 작업을 수행할 수 있다.
thenReturn이랑 thenRun은 말 그대로 리턴하고 실행시키는거고, Accept는 반환값을 받아서 사용하고, 값을 반환하지는 않는 콜백이다.

future.thenApply(result -> result * 2)
      .thenAccept(result -> System.out.println("Result: " + result));

 

비동기 콜백 등록

whenComplete, handle 등의 메서드를 통해 작업 완료 시 호출할 콜백을 등록할 수 있다.

future.whenComplete((result, exception) -> {
    if (exception == null) {
        System.out.println("Completed successfully with result: " + result);
    } else {
        System.out.println("Completed with exception: " + exception.getMessage());
    }
});

 

Thread, Future와의 차이점

그럼 기존에 존재하는 Thread, Future와의 차이점에 대해서도 알아보자.

Thread

  • 동시성 처리에 대한 낮은 수준의 제어, 즉 할 수 있는 작업이 많지 않음
  • 경쟁조건, 교착상태와 같은 오류가 발생하기 쉬움
  • 수동 예외처리
  • 직접 제어가 가능하나, 권장하지는 않는 방식임

Future

  • 작업 수행과 결과를 명확하게 구분하여 비동기식 실행을 제공
  • 비동기 프로그래밍을 단순화함
  • 여전히 명시적인 예외 처리가 필요함

CompletableFuture

  • 여러 비동기 작업들을 연결하고, 결합하는 2차 작업이 원활함
  • 내장된 예외처리 및 오류 전파가 쉬움
  • 비동기 코드로 진행하며 보다 표현적인 방법을 제공함 ex) thenReturn 등

결론


이렇게 CompletableFuture에 대해서 간단하게 알아보았다.

한 마디로 정리하자면, 완료가 확실한 작업에 대한 필터링 및 2차 작업을 하기 위한 클래스 라고 이해하면 쉬울 듯 하다.

내가 진행했던 문제는 워낙 단순한 거였어서 Thread를 써도 무방했을 것 같지만.. 너무 기본적인 것을 사용하는 것보단 더 나은 방법이 있는지 항상 고민해보고 적용하는 것을 어필하고 싶어서 CompletableFuture를 사용했다.

그런데 현업에서도 잘 안쓰인다고 한다.. 현업에서 자바로 비동기 처리를 할 땐 어떤걸 많이 쓰는지 궁금한데, 혹시 아시는 분은 댓글 달아주시면 정말 감사하겠습니다. :)

구독 및 하트는 정보 포스팅 제작에 큰 힘이됩니다♡

728x90
반응형