-
CompletableFuture (5)프로그래밍/Java 2022. 5. 6. 11:30
지난 시간에 이어 진행해보자.
이번엔 CompletableFuture 를 가지고 여러 작업을 조합하는 방법과 예외를 처리하는 방법에 대해 살펴보자.
Future 만 가지고는 특정 작업들을 이어서 처리하는게 힘들었다.
(예를들면, 이벤트 정보 가져온 다음 이벤트에 참석하는 회원 목록 가져오기 등.)
콜백을 줄 수 없었기 때문에 비동기적인 작업을 이어서 처리하기가 힘들었던 것이다.
조합하기
thenCompose( )
- 두 작업이 서로 이어서 실행하도록 조합
public class CompletableFutureStudy { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> { System.out.println("A " + Thread.currentThread().getName()); return "Hello"; }); //hello 의 결과를 받아서 compose 를 통해 특정 작업을 호출하면 결과를 future 로 받을 수 있다. CompletableFuture<String> future = hello.thenCompose(CompletableFutureStudy::getWorld); System.out.println(future.get()); } private static CompletableFuture<String> getWorld(String msg) { return CompletableFuture.supplyAsync(() -> { System.out.println("B " + Thread.currentThread().getName()); return msg + " World"; }); } } //결과 A ForkJoinPool.commonPool-worker-19 B ForkJoinPool.commonPool-worker-5 Hello World
thenCombine( )
- 두 작업을 독립적으로 실행하고 둘 다 종료 했을 때 콜백 실행
- 둘의 연관관계는 없지만 비동기적으로 실행
public class CompletableFutureStudy { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> { System.out.println("Get Apple Stock " + Thread.currentThread().getName()); return "apple $19"; }); CompletableFuture<String> world = CompletableFuture.supplyAsync(() -> { System.out.println("Get MS Stock " + Thread.currentThread().getName()); return "ms $20"; }); CompletableFuture<String> future = hello.thenCombine(world, (h, w) -> h + " | " + w); System.out.println(future.get()); } } //결과 Get Apple Stock ForkJoinPool.commonPool-worker-19 Get MS Stock ForkJoinPool.commonPool-worker-5 apple $19 | ms $20
allOf( )
- 모든 서브테스크를 다 합쳐서 실행할 경우
- 여러 작업을 모두 실행하고 모든 작업 결과에 콜백 실행
public class App { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> apple = CompletableFuture.supplyAsync(() -> { System.out.println("Get Apple Stock " + Thread.currentThread().getName()); return "apple $19"; }); CompletableFuture<String> ms = CompletableFuture.supplyAsync(() -> { System.out.println("Get MS Stock " + Thread.currentThread().getName()); return "ms $20"; }); CompletableFuture<String> aws = CompletableFuture.supplyAsync(() -> { System.out.println("Get AWS Stock " + Thread.currentThread().getName()); return "aws $30"; }); // .allOf 를 사용하여 결과값을 받아보면 null 이 출력된다. // 각 task 마다 return 타입도 다르고 에러 발생도 있을 수 있어 결과값이 의미가 없어 null 을 반환한다. CompletableFuture<Void> future = CompletableFuture.allOf(apple, ms, aws) .thenAccept(System.out::println); System.out.println(future.get()); } } //결과 Get MS Stock ForkJoinPool.commonPool-worker-5 Get Apple Stock ForkJoinPool.commonPool-worker-19 Get AWS Stock ForkJoinPool.commonPool-worker-23 null null
위 와 같이 .allOf( ) 를 사용하면 결과값을 받을 수 없다는 문제가 있다.
결과값을 받기 위해선 아래와 같이 구현해보자.
public class App { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> apple = CompletableFuture.supplyAsync(() -> { System.out.println("Get Apple Stock " + Thread.currentThread().getName()); return "apple $19"; }); CompletableFuture<String> ms = CompletableFuture.supplyAsync(() -> { System.out.println("Get MS Stock " + Thread.currentThread().getName()); return "ms $20"; }); CompletableFuture<String> aws = CompletableFuture.supplyAsync(() -> { System.out.println("Get AWS Stock " + Thread.currentThread().getName()); return "aws $30"; }); // 모든 task 들을 list 로 모아놓는다. List<CompletableFuture<String>> futures = Arrays.asList(apple, ms, aws); CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[futures.size()]); CompletableFuture<List<String>> results = CompletableFuture.allOf(futuresArray) .thenApply(v -> { // get 과 join 이 같은데, get 을 사용하면 checked exception 이 발생해서 예외처리를 해주어야 한다. // join 은 unchecked exception 이여서 예외처리를 해주지 않아도 된다. return futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList()); }); results.get().forEach(System.out::println); } } //결과 Get AWS Stock ForkJoinPool.commonPool-worker-23 Get Apple Stock ForkJoinPool.commonPool-worker-19 Get MS Stock ForkJoinPool.commonPool-worker-5 apple $19 ms $20 aws $30
anyOf( )
- 여러 작업 중에 가장 빨리 끝난 하나의 결과에 콜백 실행
public class CompletableFutureStudy { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> apple = CompletableFuture.supplyAsync(() -> { System.out.println("Get Apple Stock " + Thread.currentThread().getName()); return "apple $19"; }); CompletableFuture<String> ms = CompletableFuture.supplyAsync(() -> { System.out.println("Get MS Stock " + Thread.currentThread().getName()); return "ms $20"; }); CompletableFuture<String> aws = CompletableFuture.supplyAsync(() -> { System.out.println("Get AWS Stock " + Thread.currentThread().getName()); return "aws $30"; }); CompletableFuture<Void> future = CompletableFuture.anyOf(apple, ms, aws) .thenAccept(System.out::println); future.get(); } } //결과 Get Apple Stock ForkJoinPool.commonPool-worker-19 Get AWS Stock ForkJoinPool.commonPool-worker-23 Get MS Stock ForkJoinPool.commonPool-worker-5 apple $19
예외처리
exeptionally(Function)
public class CompletableFutureStudy { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> apple = CompletableFuture.supplyAsync(() -> { if (true) { throw new IllegalArgumentException(); } System.out.println("get apple stock " + Thread.currentThread().getName()); return "apple $19"; }).exceptionally(ex -> { System.out.println(ex); return "Error"; }); System.out.println(apple.get()); } }
handle(BiFunction)
- 정상적으로 종료되었을때 혹은 에러가 발생하였을때 둘 다 리턴값을 받을 수 있다.
public class CompletableFutureStudy { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> apple = CompletableFuture.supplyAsync(() -> { if (false) { throw new IllegalArgumentException(); } System.out.println("get apple stock " + Thread.currentThread().getName()); return "apple $19"; }).handle((result, error) -> { if (error != null) { System.out.println(error); return "Error"; } return result; }); System.out.println(apple.get()); } }
참고
백기선 - 더 자바, Java8
'프로그래밍 > Java' 카테고리의 다른 글
Metaspace (0) 2022.05.06 배열 Parallel 정렬 (0) 2022.05.06 CompletableFuture (4) (0) 2022.05.06 CompletableFuture (2) - Excutors (0) 2022.05.06 CompletableFuture (1) - 자바 Concurrent 프로그래밍 소개 (0) 2022.05.06