- ๋ชฉ์ฐจ
- CompletableFuture
- 1 Future๋ง์ผ๋ก ๋ณต์กํ ๋น๋๊ธฐ ๋ก์ง์ ๊ตฌ์ฑํ๊ธฐ ์ด๋ ต๋ค
- 2 CompletableFuture๋ฅผ ์ด์ฉํ ๊ธฐ๋ณธ์ ์ธ ๋น๋๊ธฐ ์์ฒญ
- 3 ๋น๋๊ธฐ ์์ Callback
- 4 Future์ ์กฐํฉ
- 5 ์ฌ๋ฌ Future ๋ณ๋ ฌ ์คํ
- 6 ์์ธ ์ฒ๋ฆฌ
- ์ฐธ๊ณ
์ด์ ๊ธ - ์๋ฐ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ Future์ ์ด์ด์ ์ด๋ฒ ๊ธ์ ์๋ฐ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ ๋ฆฌํ๋ ์๋ฆฌ์ฆ์ ๋๋ฒ์งธ ๊ธ์ด๋ค.
์ด์ ๊ธ์์๋ ์์๋ณด์๋ฏ์ด, JDK 1.5์ ๋์จ Future์ ํ๊ณ๊ฐ ๋ถ๋ช
ํ๋ค.
์ด๋ก์ธํด JDK 1.8๋ถํฐ CompletableFuture ์ธํฐํ์ด์ค๊ฐ ์๊ฐ๋์๊ณ , ์ด ์ธํฐํ์ด์ค๋ Future ์ธํฐํ์ด์ค๋ ๊ตฌํํ๊ธฐ๋๋ฌธ์ ๊ธฐ์กด์ Future๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ธ๋ถ์์ ์๋ฃ์ํฌ ์ ์์ผ๋ฏ๋ก CompletableFuture๋ ์ด๋ฆ์ ๊ฐ๊ฒ๋์๋ค.
CompletableFuture์ ์ฌ์ ์ ์๋ฏธ๋ "์๋ฃ๋ ๋ฏธ๋"์ด๋ค.
๊ทธ๋ฆฌ๊ณ ๋์์ CompletionStage ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํจ์ผ๋ก์จ ๋น๋๊ธฐ ์ฐ์ฐ ๋จ๊ณ(Step)๋ฅผ ์ ๊ณตํ๋ฉฐ, ์ด๋ ๋น๋๊ธฐ ์ฐ์ฐ์ ๊ณ์ํด์ ์ฒด์ด๋ ํํ๋ก ์กฐํฉํ๋ ๊ฒ์ด ๊ฐ๋ฅํด์ก๋ค.
Future์ Collection, CompletableFuture์ Stream์ ๊ด๊ณ๋ก ๋น์ ํ ์๋ ์๋ค.
์ด๋ฒ ๊ธ์ Future์ ํ๊ณ๋ฅผ ํตํด CompletableFuture๊ฐ ํ์ํ ์ด์ ๋ฅผ ์์๋ณด๊ณ , CompletableFuture์ ์ฌ์ฉ๋ฒ์ ๋ํด์ ์ ๋ฆฌํด๋ณธ๋ค.
๐โโ๏ธ Future์ ๊ฐ์ฅ ํฐ ํจ๊ณ์ ์ ๋ธ๋กํน ์ฝ๋ (get())๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์๋ Callback์ ์คํํ ์ ์๋ ๊ฒ์ด๋ค.
์ด์ ๊ธ - ์๋ฐ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ Future์์ ์ ๋ฆฌํ๋ฏ์ด, Future์ ๊ฐ์ฅ ํฐ ํ๊ณ์ ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ์ ๋ฐ๋ฅธ Callback ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ด๋ ต๋ค๋ ๊ฒ์ด๋ค.
๋ฌผ๋ก ์์ ๊ธ์ฒ๋ผ FutureTask๋ฅผ ๊ตฌํํจ์ผ๋ก์จ Callback ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ง๋ง, ์ฝ๋ฐฑ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ฝ๋๋ฅผ ๋ฐ๋ก ๋ ๊ตฌ์ฑํด์ค์ผํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์กด์ฌํ๋ค.
๐โโ๏ธ ์ด์ธ์๋ Future์ ์ฌ๋ฌ๊ฐ์ง ํ๊ณ๊ฐ ์กด์ฌํ๋ค.
- ์ฌ๋ฌ ๋น๋๊ธฐ Task ์ฒ๋ฆฌ์ ๋ํ ์์ธ ์ฒ๋ฆฌ๋ ํ์์์ ์ค์ ํ๊ธฐ ์ฝ์ง ์๋ค.
- ์ฌ๋ฌ
Future๋ฅผ ์กฐํฉํ๊ธฐ ์ด๋ ต๋ค.- ๋ณดํต ๋ ๊ฐ์ ๋น๋๊ธฐ Task ์ฒ๋ฆฌ๋ฅผ ํด์ผํ๋ค๋ฉด, ๋ ๊ฐ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ์๋ก ๋ ๋ฆฝ์ ์ผ์๋, ๋ ๋ฒ์งธ๊ฐ ์ฒซ ๋ฒ์งธ์ ์์กด์ ์ผ ์๋ ์๋ค.
Future์ ์์ ๊ฐ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ผ๋ฆฌ์ ์กฐํฉ์ ๊ตฌ์ฑํ๋๊ฒ์ ์ง์ ๊ตฌํํด์ค์ผํ๊ธฐ๋๋ฌธ์ ์ฝ์ง์๋ค.- ์ด์ธ์๋ ๋ค์ํ ์๊ตฌ์ฌํญ์ ๋ง๋
Future๋ผ๋ฆฌ์ ์กฐํฉ์ ๊ตฌ์ฑํ๊ธฐ ํ๋ค๋ค.
Future์งํฉ์ด ์คํํ๋ ๋ชจ๋ Task์ ์๋ฃ๋ฅผ ๊ธฐ๋ค๋ ค์ผํ๋ค.- ์ฝ๋์์์
Future๋ฅผ ์๋ฃ์ํฌ ์ ์๋ค.- ๊ธฐ์กด์
Future์ ์ธ๋ถ์์ ์๋ฃ์ํฌ ์ ์๋ค. ์ทจ์ํ๊ฑฐ๋,get()์ ํ์์์์ ์ค์ ํด์ค์ผํ๋ค.
- ๊ธฐ์กด์
Future์งํฉ์์ ๊ฐ์ฅ ๋นจ๋ฆฌ ์๋ฃ๋๋ Task๋ง ๊ธฐ๋ค๋ฆฌ๊ณ ๋๋จธ์ง๋ ์ค์งํ๋ ์๊ตฌ์ฌํญ์ ์งํค๊ธฐ ์ด๋ ต๋ค.- ์๋ฅผ ๋ค์ด ์ฌ๋ฌ Task๊ฐ ๋ค์ํ ๋ฐฉ์์ผ๋ก ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๊ตฌํ๋ ์ํฉ.
๐โโ๏ธ Future์ ํ๊ณ๋ฅผ ํด๊ฒฐํ CompletableFuture.
๊ธฐ์กด์ Futuru JDK 1.5๋ถํฐ ์ถ๊ฐ๋์ด ๋น๋๊ธฐ ๊ฒฐ๊ณผ๋ฅผ ์ป๋๋ฐ ์ ์ฌ์ฉ๋์์ง๋ง, ์์ ๊ฐ์ด ๊ณ์ฐ๋ค์ ๋ค์ํ ํํ๋ก ๊ฒฐํฉํ๊ฑฐ๋ ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด ๊น๋ค๋ก์ ๋ค.
๊ทธ๋ก์ธํด JDK 1.8๋ถํฐ CompletableFuture๊ฐ ์ถ๊ฐ๋์๋ค.
CompletableFuture์ ๊ธฐ์กด์ Future ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๋ฟ๋ง ์๋๋ผ, CompletionStage๋ ๊ตฌํํ๋ค.
์ด๋ CompletableFuture๊ฐ ๊ธฐ์กด์ Future ๊ธฐ๋ฅ์ ๋ชจ๋ ์ง์ํ ๋ฟ๋ง ์๋๋ผ, CompletionStage์ ๊ธฐ๋ฅ๋ ์ถ๊ฐ๋ก ์ ๊ณตํ๋ค๊ณ ์ดํดํ ์ ์๋ค.
๐โโ๏ธ CompletionStage
CompletionStage ๊ณต์ ๋ฌธ์์์ ์๋์ ๊ฐ์ด ์ค๋ช
ํ๊ณ ์๋ค.
A stage of a possibly asynchronous computation, that performs an action or computes a value when another CompletionStage completes.
A stage completes upon termination of its computation, but this may in turn trigger other dependent stages.
ํด์ํ๋ฉด CompletionStage๋ ๋ค๋ฅธ CompletionStage๊ฐ ์๋ฃ๋ ๋ ์์
์ ์ํํ๊ฑฐ๋ ๊ฐ์ ๊ณ์ฐํ๋ ํ๋์ ๋น๋๊ธฐ ๊ณ์ฐ ๋จ๊ณ(Stage)๋ผ๊ณ ๋ณผ ์ ์๋ค.
์ฆ, ์ฌ๋ฌ ๋จ๊ณ์ ๋น๋๊ธฐ Task ์ฒ๋ฆฌ๋ฅผ ๋จ๊ณ๋ณ๋ก ๊ตฌ์ฑํ์ฌ ์ฒ๋ฆฌํ ์ ์์์ ๋ํ๋ธ๋ค.
๊ฒ๋ค๊ฐ ๊ฐ ๋จ๊ณ์์ ๋ฐ์ํ ์๋ฌ๋ฅผ ๊ด๋ฆฌํ๊ณ ์ ๋ฌํ ์ ์๋ค.
๊ฐ๋จํ ์์.
stage.thenApply(x -> square(x))
.thenAccept(x -> System.out.print(x))
.thenRun(() -> System.out.println());๋น๋๊ธฐ Task ์ฒ๋ฆฌ์ ๊ฒฐ๊ณผ๋ฅผ ์ป๋
Future๋Stream๊ณผ ์ ์ฌํ ๊ฒ ๊ฐ๋ค.
CompletableFuture๋ฅผ ์ด์ฉํ์ฌ ๊ธฐ๋ณธ์ ์ธ ๋น๋๊ธฐ ์์ฒญ๊ณผ ์ฝ๋ฐฑ์ ์ง์ ๊ตฌํํด๋ณด๋ฉด์ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง ์ดํด๋ณธ๋ค.
๋น๋๊ธฐ ์์ฒญ์ ํฌ๊ฒ ์๋ ๋ ๊ฐ์ง๋ก ๋๋๋ค.
- ๋ฆฌํด๊ฐ์ด ์๋ ๊ฒฝ์ฐ:
runAsync() - ๋ฆฌํด๊ฐ์ด ์๋ ๊ฒฝ์ฐ:
supplyAsync()
runAsync()๋ ๊ฐ์ฅ ๋ํ์ ์ผ๋ก CompletableFuture๋ฅผ ์ด์ฉํ์ฌ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์์ฒญํ๋ ๋ฉ์๋๋ค.
๊ทธ๋ฆฌ๊ณ runAsync()๋ ๋น๋๊ธฐ์ ์ผ๋ก ๋์ํ๊ธธ ์ํ๋ ์์
์ค ๋ฆฌํด๊ฐ์ด ์๋ ๊ฒฝ์ฐ ์ฌ์ฉ๋๋ค.
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Hello " + currentThread().getName());
});
// get()์ ํธ์ถํด์ผ์ง๋ง, ๋น๋๊ธฐ์ ์ผ๋ก ๋์ํ๋ค.
System.out.println(future.get()); // Hello ForkJoinPool.commonPool-worker-19์์ ๊ฐ์ด ์ฝ๊ฒ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์์ฒญํ ์ ์๋ค.
runAsync()๋get()์ ํธ์ถํด์ค์ผ ์คํ๋๋ค.
๐โโ๏ธ runAsync()๋ ์
๋ ฅ๊ฐ์ผ๋ก Runnable์ ๋ฐ๋๋ค.
Runnable์ ์
๋ ฅ๊ฐ๊ณผ ๋ฆฌํด๊ฐ ๋ชจ๋ ์๋ ํจ์ํ ์ธํฐํ์ด์ค์ด๋ค.
๊ทธ๋ฌ๋ฏ๋ก runAsync()๋ ๊ฒฐ๊ณผ ๊ฐ์ด ํ์์๋ ๋น๋๊ธฐ ์์ฒญ์ ์ฌ์ฉ๋๋ค.
๐โโ๏ธ Task๋ฅผ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ ๋ ์ฌ์ฉ๋๋ ๋ํดํธ ์ค๋ ๋๋ ForkJoinPool์ด๋ค.
runAsync()์ ๋ค์์ ์ดํด๋ณผ supplyAsync()๋ชจ๋ ์คํํ ๋ ์ค๋ ๋ ํ์ ์ง์ ํด์ฃผ์ง์์ผ๋ฉด ์๋์ ๊ฐ์ด ForkJoinPool์ ์ฌ์ฉํ๋ค.
supplyAsync()๋ runAsync()์ ๊ฐ์ด ๋ํ์ ์ผ๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์์ฒญํ๋ ๋ฉ์๋์ด๋ค.
์ฐจ์ด์ ์ด๋ผ๋ฉด supplyAsync()๋ ์ด๋ฆ์์ ์ ์ ์๋ฏ์ด ๋น๋๊ธฐ์ ์ผ๋ก ๋์ํ๊ธธ ์ํ๋ ์์
์ค ๋ฆฌํด๊ฐ์ด ์๋ ๊ฒฝ์ฐ ์ฌ์ฉ๋๋ค.
ExecutorService es = Executors.newFixedThreadPool(4);
// get()์ ํธ์ถํ์ง์์๋ ๋น๋๊ธฐ๋ก Task๊ฐ ์คํ๋๋ค.
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello " + Thread.currentThread().getName(), es);
System.out.println(future.get()); // Hello pool-1-thread-1
es.shutdown();์์ ๊ฐ์ด ์ฝ๊ฒ ๋น๋๊ธฐ ์์ฒญ์ ํ ์ ์๋ค.
์ฃผ์ํ ์ ์ ๊ธฐ์กด์
Future์ ๋์ผํ๊ฒget()ํธ์ถ์ Blocking์ด ๋๋ค. ๊ทธ๋ฆฌ๊ณget()์ ํธ์ถํ์ง์์๋supplyAsync()๋ก ๋๊ฒจ์ค Task๊ฐ ๋น๋๊ธฐ๋ก ์คํ๋๋ค.
๐โโ๏ธ applyAsync()๋ ์
๋ ฅ๊ฐ์ผ๋ก Supplier์ ๋ฐ๋๋ค.
Supplier๋ ์
๋ ฅ ๊ฐ์ ์์ง๋ง, ๋ฆฌํด๊ฐ์ด ์กด์ฌํ๋ ํจ์ํ ์ธํฐํ์ด์ค์ด๋ค.
๊ทธ๋ฌ๋ฏ๋ก runAsync()๋ ๊ฒฐ๊ณผ ๊ฐ์ด ํ์ํ ๋น๋๊ธฐ ์์ฒญ์ ์ฌ์ฉ๋๋ค.
CompletableFuture์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ ํจ์ํ ํํ๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ์ Callback์ ์ง์ํ๋ค๋ ๊ฒ์ด๋ค.
CompletableFuture์ CompletionStage๋ฅผ ๊ตฌํํจ์ผ๋ก์จ ์๋ ๋ฉ์๋๋ฅผ ์ง์ํ๋ค.
thenApply(Function): ๋น๋๊ธฐ๋ก ๋์ํ ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํด๋ฐ์์ ๋ค๋ฅธ ๊ฐ์ผ๋ก ๋ฐ๊พธ๋ ์ฝ๋ฐฑStream.map๊ณผ ์ ์ฌํ๋ค.
thenAccept(Consumer): ๋น๋๊ธฐ๋ก ๋์ํ ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํด๋ฐ์์ ๋ ๋ค๋ฅธ ์์ ์ ์ฒ๋ฆฌํ๋ ์ฝ๋ฐฑ (๋ฆฌํด์์ด)thenRun(Runnable): ๋น๋๊ธฐ๋ก ๋์ํ ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํด๋ฐ์ง ์๊ณ ๋ค๋ฅธ ์์ ์ ์ฒ๋ฆฌํ๋ ์ฝ๋ฐฑ
์ ๋ฉ์๋๋ค์ ์ด์ฉํ์ฌ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์์ฒญ์ ์ฝ๋ฐฑ์ ์ ์ํ ์ ์์ผ๋ฉฐ, ์ฒด์ด๋์ ํตํด ๋ค์ํ ์กฐํฉ์ ๋ง๋ค ์ ์๋ค.
public <U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> fn) {
return this.uniApplyStage((Executor)null, fn);
}thenApply(Function)์ CompletableFuture<U>๋ฅผ ๋ฐํํ๋ฉฐ, ์ด๋ฆ์์๋ ์ ์ ์๋ฏ์ด, ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํด๋ฐ์์ ๋ค๋ฅธ ๊ฐ์ ๋ฐ๊พธ๋ ์ฝ๋ฐฑ์ด๋ค.
๋น๋๊ธฐ๋ก์ฒ๋ฆฌํ ๋ฌธ์์ด ๊ฒฐ๊ณผ๋ฅผ ๋ชจ๋ ๋๋ฌธ์์ด๋ก ๋ณํํ๋ ์์.
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();
});
// get()์ ํธ์ถํด์ผ ๋น๋๊ธฐ ์์
์ด ๋์ํ๋ค.
System.out.println(future.get()); // HELLO์ ์์์์ ๋ณผ ์ ์๋ฏ์ด, thenApply(Function)๋ Stream.map๊ณผ ๋์ผํ ์ญํ ์ ์ํํ๋ค.
thenApply๋ก ๋์ด์ค๋Function์supplyAsync()์ ๋์ผํ ์ค๋ ๋์์ ๋์ํ๋ค. ๋ณ๋์ ์ค๋ ๋์์ ์คํํ๊ณ ์ถ๋ค๋ฉดthenApplyAsync()๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return this.uniAcceptStage((Executor)null, action);
}thenAccept(Consumer)๋ CompletableFuture<Void>๋ฅผ ๋ฐํํ๋ค. ์ฆ, ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํํ์ง์๋๋ค.
์ด๋ ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํด๋ฐ์์ ๋ ๋ค๋ฅธ ์์ ์ ์ฒ๋ฆฌํ๋ ์ฝ๋ฐฑ๋ผ๊ณ ๋ณผ ์ ์๋ค.
๋จ, thenApply(Function)์๋ ๋ค๋ฅด๊ฒ ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํดํ์ง์๋๋ค. ๊ทธ์ ์ฌ์ฉํ๋ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๋ฐฑ์ผ๋ก Consumeํ๋ ์ญํ ์ ์ํํ๋ค.
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello ";
}).thenAccept((s) -> {
System.out.println(Thread.currentThread().getName());
System.out.println(s.toLowerCase());
});
// get()์ ํธ์ถํด์ผ thenAccept๊ฐ ๋์ํ๋ค.
future.get();์์์์ ๋ณผ ์ ์๋ฏ์ด, thenAccept๋ ์
๋ ฅ๊ฐ์ ๋ฐ์์ ์๋น๋งํ ๋ฟ, ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํดํ์ง์๋๋ค.
get()์ ํธ์ถํด์ผthenAccept()๊ฐ ๋์ํ๋ฉฐ,get()์ด ์์๊ฒฝ์ฐsupplyAsync()๋ก ์ฃผ์ด์ง ๋น๋๊ธฐ Task๋ง ์ํ๋๋ค.
thenAccept๋ก ๋์ด์ค๋Consumer๋supplyAsync์ ๋์ผํ ์ค๋ ๋์์ ๋์ํ๋ค. ๋ณ๋์ ์ค๋ ๋์์ ์คํํ๊ณ ์ถ๋ค๋ฉดthenAccepyAsync()๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
public CompletableFuture<Void> thenRun(Runnable action) {
return this.uniRunStage((Executor)null, action);
}thenRun(Runnable)์ ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํด๋ฐ์ง ์๊ณ ๋ค๋ฅธ ์์
์ ์ฒ๋ฆฌํ๋ ์ฝ๋ฐฑ์ด๋ค.
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
// 3์ด๊ฐ Sleep
try {
Thread.sleep(3_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello ";
}).thenRun(() -> {
System.out.println("Computation Finished");
});
future.get();์ฃผ์ํ ์ ์ ๊ธฐ์กด์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์๋ฃ๋์ด์ผ์ง thenRun์ผ๋ก ์ฃผ์ด์ง Runnable์ด ์คํ๋๋ค.
๊ณ์ฐ ๊ฐ์ด ํ์ํ์ง ์๊ฑฐ๋ ์ฒด์ธ ๋์์ ์ผ๋ถ ๊ฐ์ ๋ฐํํ์ง ์์ผ๋ ค๊ณ ํ ๋ ์ฌ์ฉ๋ ์ ์๋ค.
Callback์ ์ง์ํ๋ ๊ฒ ์ธ์๋ CompletableFuture์์ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ ์ฌ๋ฌ Future๋ฅผ ๋ณ๋ ฌ๋ก ์กฐํฉํด์ ๋น๋๊ธฐ ์์
ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑํ ์ ์๋ค๋ ๊ฒ์ด๋ค.
๋งค ์ฒด์ด๋๋ง๋ค CompletableFuture๋ฅผ ๋ฐํํ๊ธฐ๋๋ฌธ์ ์ฌ๋ฌ๊ฐ์ง ๋น๋๊ธฐ Task๋ฅผ ์ฐ๊ฒฐ ๋ฐ ๊ฒฐํฉํ ์ ์๋ค.
์๋ฅผ ๋ค์ด, 2๊ฐ์ CompletableFuture๋ฅผ ๋ณ๋ ฌ๋ก ์คํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ํฉ์น ์ ์๋ค.
์กฐํฉํ ๋ ์ฌ์ฉ๋๋ ๋ฉ์๋๋ ์๋์ ๊ฐ๋ค.
thenCompose(): ๋ ์์ ์ด ์๋ก ์ด์ด์ ์คํํ๋๋ก ์กฐํฉ.- ๋ค ์๋ฒ ์์ ์ด ์ ์๋ฒ ์์ ์ ์์กด์ ์ด๋ค.
thenCombine(): ๋ ์์ ์ ๋ ๋ฆฝ์ ์ผ๋ก ์คํํ๊ณ ๋ ๋ค ์ข ๋ฃ ํ์ ๋ ์ฝ๋ฐฑ์ ์คํ.
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
return this.uniComposeStage((Executor)null, fn);
}thenCompose()๋ ๋ ๊ฐ์ Future๋ฅผ ์์ฐจ์ ์ผ๋ก ์ฐ๊ฒฐํ๋ค.
CompletableFuture<String> combinedFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello ";
}).thenCompose((result) -> CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("World " + Thread.currentThread().getName());
return result + " World";
}));
System.out.println(combinedFuture.get());
// Hello ForkJoinPool.commonPool-worker-19
// World ForkJoinPool.commonPool-worker-19
// Hello World๋ค์ ์ฃผ์ด์ง Future (์ ์์์์ World)๊ฐ ์ ์๋ฒ์ Future (์ ์์์์ Hello)๊ฒฐ๊ณผ์ ์์กด์ ์ด๋ฏ๋ก, ์์ฐจ์ ์ผ๋ก Future๊ฐ ์คํ๋๋ค.
thenApply()์ thenCompose()๊ฐ ํ๋ ์ญํ ์ด ๋น์ทํ๋ค๋ณด๋ ๋ ๋ฉ์๋๊ฐ ํฐ ์ฐจ์ด๊ฐ ์์ด๋ณด์ธ๋ค.
public <U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> fn)
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)๋ฉ์๋ ์๊ทธ๋์ฒ์์ ์ ์ ์๋ฏ์ด, ๋ ๋ฉ์๋๋ ๋ฐํ๊ฐ์ ๊ฐ์ง๋ง ๋งค๊ฐ๋ณ์์ ํ์ ์ด ๋ค๋ฅด๋ค.
thenApply()๋Future๊ฒฐ๊ณผ๋ฅผ ๋ฐ์์ ๋, ๋ฐํ ์ ์ ์ด๋ค ์ฒ๋ฆฌ๋ฅผ applyํ ๋ ์ฌ์ฉ๋๋ค.map๊ณผ ๊ฐ์ดFuture์ ๊ฒฐ๊ณผ๋ฅผ ํน์ ๊ฐ์ผ๋ก ๋ณํํ ๋ ์ฌ์ฉ๋ ์ ์๋ค. (๋ฐํ ๊ฐ์ด ์ด์ Future๋ก ์ฒ๋ฆฌํ ๊ฒฐ๊ณผ์ด๋ค.)
thenCompose()๋Future๋ค์์ ๋ ๋ค๋ฅธFuture๋ฅผ ์ด์ด์ ์คํํ๊ฒ๋ ์ฐ๊ฒฐํ ๋ ์ฌ์ฉ๋๋ค.flatmap๊ณผ ๊ฐ์ด ๋ ๋ค๋ฅธ CompletableFuture๋ฅผ ํ์ดํ๋ผ์ธ ํ์์ผ๋ก ์ฐ๊ฒฐํด์ ์คํํ ์ ์๊ฒ ํ๋ค. (๋ฐํ ๊ฐ์ด ์๋ก์ด ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๊ฒฐ๊ณผ์ธCompletionStageํ์ ์ด์ด์ผํ๋ค..)
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
return this.uniComposeStage((Executor)null, fn);
}thenCompose()๋ ๋ ์์
์ด ์๋ก ์ด์ด์ ์คํ๋๋๋ก ์กฐํฉํ๊ธฐ๋๋ฌธ์, ๋ท ์๋ฒ์ Task๊ฐ ์ ์๋ฒ์ Task๋ฅผ ์์กดํ๋ค.
๋ฐ๋ฉด์, thenCombine()์ ๋ ๊ฐ์ Task๋ฅผ ์๋ก ๋
๋ฆฝ์ ์ผ๋ก ์คํํ๋ค.
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
});
CompletableFuture<String> world = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1_500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("World " + Thread.currentThread().getName());
return "World";
});
CompletableFuture<String> future = hello.thenCombine(world, (h_result, w_result) -> {
return h_result + " " + w_result;
});
System.out.println(future.get());
// World ForkJoinPool.commonPool-worker-5
// Hello ForkJoinPool.commonPool-worker-19
// Hello WorldthenCombine()์ ๋ Task๊ฐ ์๋ก ๋
๋ฆฝ์ ์ด๋ฏ๋ก, ์๋ฒ์ ์๊ด์์ด ๋์์ ๋น๋๊ธฐ๋ก ์คํํ๊ณ ๋์ ๋ ๋ค ์๋ฃ๋์์๋ Combine๋๋ค.
thenAcceptBoth()๋ ๋ ๊ฐ์ Future ๊ฒฐ๊ณผ๋ก ๋ฌด์ธ๊ฐ๋ฅผ ํ๊ณ ์ถ์ง๋ง, ๊ฒฐ๊ณผ ๊ฐ์ Future ์ฒด์ธ์ผ๋ก ์ ๋ฌํ ํ์๊ฐ์์ ๋ ์ฌ์ฉ๋๋ค.
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello ";
}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("World " + Thread.currentThread().getName());
return "World";
}), (s1, s2) -> System.out.println(s1 + s2));
completableFuture.get();
// World ForkJoinPool.commonPool-worker-5
// Hello ForkJoinPool.commonPool-worker-19
// Hello World๋ ๊ฐ์ Future ๊ฒฐ๊ณผ๋ก Comsume๋งํ๊ณ ์ถ์๋ ์ฌ์ฉ๋๋ค.
๋ํ, ๋ Task๊ฐ ์๋ก ๋ ๋ฆฝ์ ์ด๋ฏ๋ก, ์๋ฒ์ ์๊ด์์ด ๋์์ ๋น๋๊ธฐ๋ก ์คํํ๊ณ ๋์ ๋ ๋ค ์๋ฃ๋์์ ๋ Comsume๋๋ค.
CompletableFuture์ ์ฌ๋ฌ Future๋ฅผ ์กฐํฉํด์ ๋น๋๊ธฐ ์์
ํ์ดํ๋ผ์ธ์ ๋ง๋ค ์ ์์ ๋ฟ๋ง ์๋๋ผ, ์ฌ๋ฌ Future๋ฅผ ๋ณ๋ ฌ๋ก ์คํํ๊ณ ๋ชจ๋ Future๊ฐ ์คํ๋ ๋๊น์ง ๊ธฐ๋ค๋ ค์ ๊ฒฐํฉ๋ ๊ฒฐ๊ณผ๋ฅผ ํ๋ฒ์ ์ป๋ ๋ฐฉ๋ฒ๋ ์ ๊ณตํ๋ค.
์ฝ๊ฒ ๋งํด.. CompletableFuture์ ์ฌ๋ฌ Future๋ฅผ ๋ณ๋ ฌ๋ก ์คํํ ์ ์๊ฒํด์ฃผ๋ ๋ ๊ฐ์ง ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
allOf(): ์ฌ๋ฌ Task๋ฅผ ๋ชจ๋ ์คํํ๊ณ ๋ชจ๋ ์์ ๊ฒฐ๊ณผ์ ์ฝ๋ฐฑ ์คํ.anyOf(): ์ฌ๋ฌ Task์ค์ ๊ฐ์ฅ ๋นจ๋ฆฐ ๋๋ ํ๋์ ๊ฒฐ๊ณผ์ ์ฝ๋ฐฑ ์คํ.
allOf()๋ ์ฌ๋ฌ ์์
์ ์กฐํฉํ๋ฉฐ, ์ฌ๋ฌ Task๋ฅผ ๋ชจ๋ ์คํํ๊ณ ๋ชจ๋ ์์
๊ฒฐ๊ณผ์ ์ฝ๋ฐฑ ์คํํ๋ค.
allOf() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋งค๊ฐ๋ณ์๋ก ์ ๊ณต๋ ๋ชจ๋ Future๊ฐ ์๋ฃ๋ ๋๊น์ง Blocking ๋๋ค.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Beautiful");
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2, future3);
// ... ๋น๋๊ธฐ ์์ฒญํ ๋ฐ๋ก ์ฒ๋ฆฌํด์ผํ ์ฝ๋๊ฐ ์์ผ๋ฉด ์ฌ๊ธฐ์ ์ ์ํ๋ฉด ๋๋ค. ...
// allOf()๋ get()์ ํธ์ถํ๋ฉด ๋งค๊ฐ๋ณ์๋ก ๋์ด์จ ๋ชจ๋ Future๊ฐ ์๋ฃ๋ ๋๊น์ง Blocking๋๋ค.
combinedFuture.get();
// get()์ Blocking์ด ํ๋ฆฌ๋ฉด, allOf()์ ๋งค๊ฐ๋ณ์๋ก ๋์ด์จ ๋ชจ๋ Future๋ ์๋ฃ๋ ์ํ์ด๋ค.
assertTrue(future1.isDone());
assertTrue(future2.isDone());
assertTrue(future3.isDone());์ด๋ ์ฌ๋ฌ Future์ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ๋ ์ป๊ธฐ์ํด ํ๋ฒ์ Blockingํ ๋ ์์ฃผ ์ ์ฉํ๋ค.
๐โโ๏ธ ํ๊ฐ์ง ์ฃผ์ํ ์ ์ผ๋ก CompletableFuture.allOf()์ ๋ฐํ ์ ํ์ CompletableFuture<Void>์ด๋ค.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<Void> combinedFutures = CompletableFuture.allOf(future1, future2)
.thenAccept(System.out::println);
System.out.println(combinedFutures);
// null์์ ๊ฐ์ด allOf() ๊ฒฐ๊ณผ๋ฅผ ์ง์ ๋ฐํํ์ง ์๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก ์๋์ ๊ฐ์ด ๋ฉ์๋ ์๊ทธ๋์ฒ๋ฅผ๋ณด๋ฉด CompletableFuture<Void>๋ฅผ ๋ฐํํ๋ค.
์ด๋ allOf()์ ํ๊ณ์ด๊ธฐ๋ํ๋ฐ.. allOf()๋ ์์ ๊ฐ์ด ๋งค๊ฐ๋ณ์๋ก ์ฃผ์ด์ง ๋ชจ๋ CompletableFuture์ ๊ฒฐํฉ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ์ง ์๋๋ค.
๊ทธ ์ด์ ๋ ๊ฐ๊ฐ์ CompletableFuture๊ฐ ๋ฐํํ๋ ํ์
์ด ์๋ก ๋ค๋ฅผ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฉ์๋์ ์ฃผ์๋ถ๋ถ์๋ ์์ ๊ฐ์ด ๊ฐ ๊ฐ๋ณ CompletableFuture์ ์์ธ ์ฒ๋ฆฌ์ ๊ฒฐ๊ณผ๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๊ฐ์ ์ฒ๋ฆฌํด์ค์ผํ๋ค๊ณ ํ๋ค.
์๋ฅผ ๋ค์ด, future1์ String์ future2์ Integer๋ฅผ ๋ฐํํ๋ค๋ฉด, ํ๋์ ๊ฒฐํฉ๋ ๊ฒฐ๊ณผ๋ก ๋ฐํํ ์ ์๊ธฐ๋๋ฌธ์ ์ด์ฉ ์ ์์ด Void๋ฅผ ๋ฐํํ๊ณ ์๋์ผ๋ก ๊ฐ ์ ์ฒ๋ฆฌํด์ค์ผํ๋ ๊ฒ์ด๋ค.
์ด ๋๋ฌธ์..
allOf๋ฅผ ํตํด ์๋ก ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋CompletableFuture๋ฅผ ์ฌ์ฉํ ์ ์ฝ๋๊ฐ ๋น๊ต์ ๋๋ฌ์์ง๋ค.
๐โโ๏ธ ๋ง์ฝ ๋ชจ๋ Future์ ๊ฒฐํฉ๋ ๊ฒฐ๊ณผ๋ฅผ ์ป๊ณ ์ถ์ผ๋ฉด ์๋์ผ๋ก ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์์ผํ๋ค.
๋คํํ JDK 1.8์ CompletableFuture๋ join() ๋ฉ์๋๋ฅผ ์ง์ํจ์ผ๋ก์จ ๋น๊ต์ ๊ฐ๋จํ allOf()์ ๋งค๊ฐ๋ณ์๋ก ์ฃผ์ด์ง ๋ชจ๋ Future์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์๊ฒํ์๋ค.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Beautiful");
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "World");
combinedFuture.get();
String combined = Stream.of(future1, future2, future3)
.map(CompletableFuture::join)
.collect(Collectors.joining(" "));
assertEquals("Hello Beautiful World", combined);๐ค get()๊ณผ join()์ ์ฐจ์ด์ ์?
Future.get()๊ณผ CompletableFuture.join()์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ ์์ธ๋ฅผ ๋์ง๋ ๋ฐฉ์์ ์๋ค.
// Future.get()
V get() throws InterruptedException, ExecutionException;
// CompletableFuture.join()
public T join()get()์ ๋ ๊ฐ์ CheckedException์ ๋์ง๊ธฐ๋๋ฌธ์ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ์ธก์์ ์์ธ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ด์ผํ๋ค.
๋ฐ๋ฉด์, join()์ ์ด๋ ํ CheckedException์ ๋์ง์ง์๊ธฐ๋๋ฌธ์ ํฌ๋ผ๋ฆฌ์ธํธ์์ ๋ฐ๋ก ์์ธ์ฒ๋ฆฌํด์ค ํ์๊ฐ์๋ค.
๋์ CompletionException๋ผ๋ UncheckedException๋ฅผ ๋์ง๋๋ฐ, ์ด๋ exceptionally()๋ก ์์ธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค.
๋ ์์ธํ ๋ด์ฉ์ ์ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ .
๐โโ๏ธ ์ฌ๋ฌ Future์ ๋ฐํ ๊ฐ์ด ๊ฐ์ ๊ฒฝ์ฐ ๊ฐ๊ฐ์ ๊ฒฐ๊ณผ ๊ฐ์ ์ป์ด์ค๋ ์์
์ฌ๋ฌ Future์ ๊ฒฐ๊ณผ ๊ฐ์ ๊ฒฐํฉํ์ง์๊ณ ๊ฐ๊ฐ ์ป์ด์ค๊ณ ์ถ์๋ ์ฌ์ฉ๋๋ ์์์ด๋ค.
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";
});
List<CompletableFuture<String>> futures = Arrays.asList(hello, world);
CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[futures.size()]);
// allOf()๋ futuresArray๋ก ์ฃผ์ด์ง Future๋ค์ด ๋ชจ๋ ๋๋ฌ๋ค๋ ๊ฒ์ ๋ณด์ฅํ๋ค.
// ๊ทธ๋ฌ๋ฏ๋ก thenApply๋ก ์ฃผ์ด์ง ์ฝ๋ฐฑ์ด ์คํ๋ ๋ ์ด๋ฏธ ๋ชจ๋ Future๊ฐ ์๋ฃ๋ ์ํ์ด๋ค.
// v๋ Void์ ์ฝ์๋ก, ์๋ฌด ์๋ฏธ์๋ค.
CompletableFuture<List<String>> results = CompletableFuture.allOf(futuresArray)
.thenApply(v -> futures.stream()
.map(stringCompletableFuture -> stringCompletableFuture.join())
.collect(Collectors.toList()));
results.get().forEach(System.out::println);
// World ForkJoinPool.commonPool-worker-19
// Hello ForkJoinPool.commonPool-worker-5
// Hello
// WorldallOf()๋ฅผ ํตํด ๋ชจ๋ ์์
์ด ์๋ฃ๋์์์ ๋ณด์ฅ๋ฐ๊ณ ๋๋ฉด, thenApply()๋ฅผ ํตํด ์ง์ allOf()์ ๋งค๊ฐ๋ณ์๋ก ๋๊ธด CompletableFuture์์ join()๋ฅผ ํตํด ๊ฐ์ ๊บผ๋ด์ค์ผํ๋ค.
์ด๋ฅผ ๋ฉ์๋๋ก ๋ถ๋ฆฌํด์ ์ฌํ์ฉํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด ๊ตฌํํ๋ฉด๋๋ค.
public class Futures {
public static <T> CompletableFuture<List<T>> all(List<CompletableFuture<T>> futures) {
CompletableFuture<Void> cfv = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
return cfv.thenApply(future -> {
return futures.stream()
.map(completableFuture -> completableFuture.join())
.collect(Collectors.toList());
});
}
}anyOf()๋ allOf()์ ๋์ผํ ํน์ง์ ๊ฐ์ง์ง๋ง, allOf()์ ๋ค๋ฅด๊ฒ ์ฌ๋ฌ Future์ค ๊ฐ์ฅ ๋นจ๋ฆฌ ๋๋ ํ๋์ ๊ฒฐ๊ณผ๋ง ๊ฐ์ ธ์จ๋ค.
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<Void> future = CompletableFuture.anyOf(hello, world).thenAccept(System.out::println);
future.get();์ ์์์ ์ถ๋ ฅ ๊ฒฐ๊ณผ๋ Hello ํน์ World์ค ๊ฐ์ฅ ๋จผ์ ๋๋ Task ํ๋๋ง ์ถ๋ ฅ๋๋ค.
๋น๋๊ธฐ๋ก ์์ฒญํ Task์์ ์ธ์ ๋ ์์ธ๊ฐ ๋ฐ์ํ ์ ์๋ค. ์ด๋ฌํ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ํด CompletableFuture์ ์๋ ๋ ๊ฐ์ง ๋ฉ์๋๋ฅผ ์ง์ํ๋ค.
- exceptionally(Function): Task๊ฐ ์์ธ๋ก ์ธํด ์๋ฃ๋์์ ๋ ์์ธ์ฒ๋ฆฌ๋ฅผ ์ํ ์ฝ๋ฐฑ.
- handle(BiFunction): Task๊ฐ ์ ์ ํน์ ์์ธ๋ก ์๋ฃ๋์์ ๋์ ์ฝ๋ฐฑ
CompletableFuture์ ์์ธ ์ฒ๋ฆฌ๋ ์๋์ ๊ฐ์ด ๊ต์ฅํ ๊ฐ๋จํ๋ค.
boolean throwError = true;
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (throwError) {
throw new IllegalArgumentException();
}
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).exceptionally(ex -> {
System.out.println(ex); // java.util.concurrent.CompletionException: java.lang.IllegalArgumentException
return "ERROR!";
});
System.out.println(future.get()); // ERRORsupplyAsync, thenApply, thenAccept ๋ฑ์ ๋ฉ์๋์์ ์์ธ๊ฐ ๋ฐ์ํ์ ๋ exceptionally()๋ฅผ ํตํด ์์ธ๋ฅผ ํธ๋ค๋งํ ์ ์๋ค.
handle๋ฉ์๋๋ฅผ ์ด์ฉํ๋ฉด Future๊ฐ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ ๋์ ์์ธ๊ฐ ๋ฐ์ํ์ฌ ์ฒ๋ฆฌ๋์์๋์ ์ฝ๋ฐฑ์ ํ๋ฒ์ ์ ์ํ ์ ์๋ค.
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());


