✍️ CompletableFuture, 작업의 조합
Future만으론 작업을 이어서 수행하는 것이 어려웠다.
= Callback이 없었기에 비동기적인 작업 두 개를 연결하는 것 자체가 어려웠다.
가령, hello가 끝나고 world를 해야 한다면 두 번의 get 호출이 이어져야 했다.
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
System.out.println("hello " + Thread.currentThread().getName());
return "hello";
});
CompletableFuture<String> world = CompletableFuture.supplyAsync(() -> {
System.out.println("world! " + Thread.currentThread().getName());
return "world!";
});
String res1 = hello.get();
String res2 = world.get();
System.out.println(res1 + res2);
// hello ForkJoinPool.commonPool-worker-1
// world! ForkJoinPool.commonPool-worker-2
// helloworld!
thenCompose
반면 CompletableFuture의 thenCompose를 사용하면 연관관계가 있는 비동기 작업을 이어서 수행할 수 있다.
thenCompose는 두 작업 간의 의존성이 필요할 때 사용된다. 예를 들어 A 작업이 수행된 다음 B 작업을 수행해야 하는 상황
CompletableFuture<String> helloworld = CompletableFuture.supplyAsync(() -> {
System.out.println("hello " + Thread.currentThread().getName());
return "hello";
}).thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "world"));
System.out.println(helloworld.get());
// hello ForkJoinPool.commonPool-worker-1
// helloworld
thenCombine
thenCombine은 연관관계가 없는 독립적인 작업을 조합할 때 사용된다.
가령 관계가 없는 A, B 두 작업을 독립적으로 수행하고 양쪽의 결과가 완료됐을 때 결과물을 BiFunction에서 처리한다.
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
System.out.println("hello " + Thread.currentThread().getName());
return "hello";
});
CompletableFuture<String> world = CompletableFuture.supplyAsync(() -> {
System.out.println("world! " + Thread.currentThread().getName());
return "world!";
});
CompletableFuture<String> helloworld = hello.thenCombine(world, (h, w) -> h + w);
System.out.println(helloworld.get());
// hello ForkJoinPool.commonPool-worker-2
// world! ForkJoinPool.commonPool-worker-2
// helloworld!
allOf
작업이 두 개 이상일 때 모든 작업을 합쳐서 수행하는 방법으로 allOf 메서드가 있다.
allOf는 여러 작업을 합쳐서 수행한다. 그렇다면 A 작업의 결과는 String, B 작업의 결과는 void, C 작업의 결과는 Integer 일 수 있다. 그렇기에 allOf로 조합한 작업의 결과는 항상 void를 반환한다.
만약 각 작업의 결과를 반환받고 싶다면 futures의 stream을 열어 결괏값을 List<Object>로 받을 수 있다.
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
return "hello";
});
CompletableFuture<Void> empty = CompletableFuture.runAsync(() -> {
});
CompletableFuture<Integer> three = CompletableFuture.supplyAsync(() -> {
return 3;
});
CompletableFuture[] futuresArray = { hello, empty, three };
List<CompletableFuture> futures = Arrays.asList(futuresArray);
CompletableFuture<List<Object>> results = CompletableFuture.allOf(futuresArray).thenApply(v -> {
return futures.stream()
.map(o -> o.join()) // ignore unchecked exception
.collect(Collectors.toList());
});
results.get().forEach(System.out::println);
// hello
// null
// 3
anyOf
anyOf는 여러 작업가운데 가장 먼저 끝난 작업의 결과를 반환한다.
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
return "hello";
});
CompletableFuture<Integer> world = CompletableFuture.supplyAsync(() -> {
return 3;
});
CompletableFuture<Object> future = CompletableFuture.anyOf(hello, world);
System.out.println(future.get());
// hello or 3
🍊 CompletableFuture, 예외 처리
exceptionally(Function)
비동기 작업에서 에러가 발생한다면 exceptionally에서 에러 타입을 받고 무언가를 반환할 수 있다.
boolean throwError = true;
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (throwError)
throw new IllegalArgumentException();
System.out.println("hello" + Thread.currentThread().getName());
return "hello";
}).exceptionally(e -> {
System.out.println(e);
return "error!";
});
System.out.println(future.get());
// java.util.concurrent.CompletionException: java.lang.IllegalArgumentException
// error!
handle(BiFunction)
handle은 exceptionally보다 일반적으로 사용되는 메서드로 정상적으로 실행되는 상황과 에러가 발생했을 때의 상황 모두를 다룬다.
boolean throwError = true;
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (throwError)
throw new IllegalArgumentException();
System.out.println("hello" + Thread.currentThread().getName());
return "hello";
}).handle((result, ex) -> {
if (ex != null) {
System.out.println(ex);
return "error!";
}
return result;
});
System.out.println(future.get());
// java.util.concurrent.CompletionException: java.lang.IllegalArgumentException
// error!
'Java > Java 8' 카테고리의 다른 글
[Java8] List를 Map으로 변환 (List to Map) (0) | 2022.08.16 |
---|---|
[Java8] List를 Set으로 변환 (List to Set) (0) | 2022.08.16 |
[Java8] Chapter 6-4. CompletableFuture vs Future (0) | 2022.03.16 |
[Java8] Chapter 6-3. Callable과 Future (0) | 2022.03.02 |
[Java8] Chapter 6-2. Executors (0) | 2022.03.01 |