Java

[Java8] CompletableFuture

나프초 2024. 9. 1. 21:27

CompletableFuture란?

  • 자바에서 비동기(Asynchronous) 프로그래밍을 가능하게 하는 인터페이스
  • 일반적인 Future 사용시 제약이 많았음

 

Future 단점

  • 외부에서 Future를 완료시킬 수 없다.
    • 취소하거나 get()에 타임아웃을 설정할 수 는 있다.
  • 블록킹 코드( get() )을 사용하지 않고서는 작업이 끝났을 때 콜백을 실행할 수 없다.
  • 여러 Future를 조합할 수 없다.
  • 예외처리용 API를 제공하지 않는다.

 

CompletableFuture

  • 두가지 인터페이스를 구현하고 있다.
    • Implements Future
    • Implements CompletionStage
  • 외부에서 명시적으로 완료시켜버릴 수 있다.
    • 예를 들어 몇초 이내 응답이 안오면 기본 값으로 미리 세팅해둔 값을 리턴하도록 만들 수 있다.
  • 명시적으로 ExecutorService를 만들어 쓸 필요가 없고, CompletableFuture만 가지고 비동기적으로  어떤 작업들을 실행할 수 있다.
  • get()을 호출하지 않으면 아무 일도 일어나지 않는다.

 

비동기로 작업 실행

  • 리턴 값 없는 경우 : runAsync()
  • 리턴 값 있는 경우 : supplyAsync()
  • 원하는 Executor(쓰레드풀)을 사용하여 실행할 수 있다. (기본은 ForkJoinPool.commonPool() 이고, runAsync() 혹은 supplyAsync()의 두번째 파라미터로 전달하면 된다.)
public static  void main(String[] args) throws ExecutionException, interruptedException {
	CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
    	System.out.println("Hello " + Thread.currentThread().getName());
    });
    
    future1.get();
    /* 출력
    Hello ForkJoinPool.commonPool-worker-3
    */
    
	CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    	System.out.println("Hello " + Thread.currentThread().getName());
        return "Hello";
    });
    
    System.out.println(future2.get());
    /* 출력
    Hello ForkJoinPool.commonPool-worker-3
    Hello
    */
}

 

콜백 제공

  • 콜백도 원하는 Executor(쓰레드풀)을 사용하여 실행할 수 있다. (기본은 ForkJoinPool.commonPool() 이고, thenApply(), thenAccept(), thenRun()의 두번째 파라미터로 전달하면 된다.)
  • thenApply(Function) : 리턴값을 받아 다른 값으로 바꾸는 콜백
//thenApply
public static  void main(String[] args) throws ExecutionException, interruptedException {
	CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    	System.out.println("Hello " + Thread.currentThread().getName());
        return "Hello";
    }).thenApply((s) -> {	//리턴값을 받아
    	System.out.println(Thread.currentThread().getName());
        return s.toUpperCase();	//다른 값으로 바꾸어 콜백
    });
    
    System.out.println(future.get());
}

/* 출력
Hello ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-3
HELLO
*/

 

  • thenAccept(Consumer) : 리턴값을 받아 다른 작업을 하는 콜백(다른 작업을 처리할 때는 리턴하지 않는다)
//thenAccept
public static  void main(String[] args) throws ExecutionException, interruptedException {
	CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    	System.out.println("Hello " + Thread.currentThread().getName());
        return "Hello";
    }).thenApply((s) -> {	//리턴값을 받아
    	System.out.println(Thread.currentThread().getName());
    	System.out.println(s.toUpperCase());	//다른 작업을 하는 콜백(리턴은 하지 않음)
    });
    
    future.get();
}

/* 출력
Hello ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-3
HELLO
*/

 

  • thenRun(Runnable) : 리턴값을 받지 않고 다른 작업을 하는 콜백(사실상 runnable과 같음)
//thenRun (그냥 runnable이 오는 것과 같음)
public static  void main(String[] args) throws ExecutionException, interruptedException {
	CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    	System.out.println("Hello " + Thread.currentThread().getName());
        return "Hello";
    }).thenApply(() -> {	//리턴값을 받지 않고
    	System.out.println(Thread.currentThread().getName());//다른 작업을 하는 콜백(리턴도 하지 않음)
    });
    
    future.get();
}

/* 출력
Hello ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-3
*/

 

조합하기

  • thenCompose() : A 의 결과를 받아 B 작업이 이어서 실행되도록 조합(서로 연관 관계가 있는 경우)
  • thenCombine() : 두 작업을 독립적으로(비동기로) 실행하고, 둘 다 종료되었을 때 콜백 실행(서로 연관 관계가 없는 경우)
  • allOf() : 여러(두개 이상) 작업을 모두 실행하고 모든 작업 결과에 콜백 실행
    • 모든 작업들의 결과가 동일한 타입이라는 보장도 없고 에러가 발생하는 작업이 생길 수 도 있다.
  • anyOf() : 여러 작업중에 가장 빨리 끝난 하나의 결과에 콜백 실행

 

예외 처리

  • exceptionally(Function)
  • handle(BiFunction)

이 포스팅은 더 자바, Java 8 강의를 수강하며 작성되었습니다.

 

더 자바, Java 8 강의 | 백기선 - 인프런

백기선 | 자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합

www.inflearn.com