CompletableFuture๋ฅผ ํ์ฉํ ๋น๋๊ธฐ์์ ์ฝ์์ ๋ก๊ทธ๊ฐ ๋จ์ง ์๋ ๋ฌธ์
๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํด CompletableFuture ์ฌ์ฉ๋ฒ์ ์ตํ๋ ์ค, ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ก์ง(Task ํด๋์ค์ doAsyncWithForkJoinPool( ))์์ ์ฝ์์ ๋ก๊ทธ๊ฐ ๋จ์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์ด์ ๋ํด ์์๋ณด๊ณ ์ ํฉ๋๋ค.
๋ฌธ์ ๊ฐ ๋์๋ ์ฝ๋๋ก ์คํ ๊ฒฐ๊ณผ๋ "๋ฉ์ธ ์ข ๋ฃ" ๋ง ๋์ฌ ๋ฟ, Thread.currentThread().getName() ๊ด๋ จ ๋ก๊ทธ๋ ์ฝ์์ ๋์ค์ง ์์์ต๋๋ค.
// Main ํด๋์ค
public static void main(String[] args) {
Task task = new Task();
CompletableFuture<Void> future = task.doAsyncWithForkJoinPool().thenAccept(s -> {
System.out.println("[main] Thread.currentThread().getName() = "
+ Thread.currentThread().getName());
System.out.println(s);
});
System.out.println("๋ฉ์ธ ์ข
๋ฃ");
}
// Task ํด๋์ค
public CompletableFuture<String> doAsyncWithForkJoinPool() {
return CompletableFuture.supplyAsync(() -> {
System.out.println("[doAsync] Thread.currentThread().getName() = "
+ Thread.currentThread().getName());
return "doAsync";
});
}
CompletableFuture๋?
CompletableFuture๋ Java 8๋ถํฐ ์ ๊ณต๋ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ํ ๊ฐ๋ ฅํ ๋๊ตฌ์
๋๋ค.
์ด๋ฅผ ํตํด ๋น๋๊ธฐ ์์
์ ์ํ์ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์์ผ๋ฉฐ, ๋ค์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค:
- ์ฝ๋ฐฑ ์ถ๊ฐ: ๋น๋๊ธฐ ์์ ์ด ์๋ฃ๋ ํ ์คํํ ์์ ์ ๋ฑ๋ก.
- ์์ ์กฐํฉ: ์ฌ๋ฌ ๋น๋๊ธฐ ์์ ์ ์กฐํฉํด ์๋ก์ด ์์ ์ ์์ฑ.
- ์์ธ ์ฒ๋ฆฌ: ๋น๋๊ธฐ ์์ ์ค ๋ฐ์ํ ์์ธ๋ฅผ ๊ฐํธํ๊ฒ ์ฒ๋ฆฌ.
๊ธฐ๋ณธ์ ์ผ๋ก CompletableFuture๋ฅผ ํตํด ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํ๋ฉด ForkJoinPool.commonPool()์ ์์ปค ์ค๋ ๋๊ฐ ๋น๋๊ธฐ ์์ ์ ์คํํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ , ์ด ForkJoinPool ์ด ๋ฌธ์ ์ ์์ธ์ด์์ต๋๋ค.
ForkJoinPool์ ์ฐ๋ ๋ ์์ฑ ์๋ฆฌ
ForkJoinPool์ ์์ปค ์ค๋ ๋๋ ๋ชจ๋ ๋ฐ๋ชฌ ์ค๋ ๋๋ก ์ด๊ธฐํ๋๋ค๊ณ ๋ช ์๋์ด ์์ต๋๋ค.
All worker threads are initialized with Thread.isDaemon set true.
๋ํ, ForkJoinPool.commonPool()์ Javadoc์ ๋ฐ์ค์ ํด์ํ์๋ฉด ๋น๋๊ธฐ ์์ ์ด ํ๋ก๊ทธ๋จ ์ข ๋ฃ ์ ์ ๋ฐ๋์ ์๋ฃ๋์ด์ผ ํ๋ ํ๋ก๊ทธ๋จ์์๋ commonPool().awaitQuiescence๋ฅผ ํธ์ถํด์ผ ํ๋ค๊ณ ๋์์์ต๋๋ค.
Returns the common pool instance. This pool is statically constructed; its run state is unaffected by attempts to shutdown or shutdownNow. However this pool and any ongoing processing are automatically terminated upon program System.exit. Any program that relies on asynchronous task processing to complete before program termination should invoke commonPool().awaitQuiescence, before exit.
Returns: the common pool instance Since: 1.8
๋ํ, ForkJoinPool ์ ๋ณ๋ ฌ ์คํธ๋ฆผ(parallel())์ ์ฌ์ฉํ ๋ ์ฃผ๋ก ๋์ค๋ ์ฉ์ด์
๋๋ค. ๋ณ๋ ฌ ์คํธ๋ฆผ์ ์ฌ์ฉํ ๋, ForkJoinPool() ์ ์ฐ๋ ๋ ์๋ Runtime.availableProcessors ์ ๋ฐํ๊ฐ์ผ๋ก ํ์ ์ฌ์ฉํ ์ค๋ ๋ ์๋ฅผ ๊ฒฐ์ ํฉ๋๋ค. (์ถ์ฒ: ๋ชจ๋ ์๋ฐ ์ธ ์ก์
ไธญ). ๋ณ๋ ฌ ์คํธ๋ฆผ์ ๊ดํด์๋ ์ด๋ฒ ๋ด์ฉ๊ณผ ๋ฌด๊ดํด์ ์ด๋ฒ ๊ธ์์๋ ์ค๋ช
ํ์ง ์๊ฒ ์ต๋๋ค.
๋ฐ๋ชฌ ์ค๋ ๋ ์ค์ํ๊ฐ?
JVM์ ๋ชจ๋ ์ฌ์ฉ์ ์ค๋ ๋๊ฐ ์ข
๋ฃ๋์์ ๋ ์ข
๋ฃ๋ฉ๋๋ค.
์ฆ, ๋ฐ๋ชฌ ์ค๋ ๋๋ ์ฌ์ฉ์ ์ค๋ ๋๊ฐ ์ข
๋ฃ๋๋ฉด ์์
์ค์ด๋ผ๋ JVM๊ณผ ํจ๊ป ๊ฐ์ ๋ก ์ข
๋ฃ๋ฉ๋๋ค.
Main ์ฐ๋ ๋๊ฐ ์ข ๋ฃ๋ ๋, JVM์ด ์ข ๋ฃ๋๋ ๊ฒ์ด ์๋๋๋ค.(์ถ์ฒ - ๊น์ํ๋์ ๊ณ ๊ธ ์๋ฐ 1ํธ ไธญ)
๋ฐ๋ผ์, ๋ฐ๋ชฌ ์ค๋ ๋๊ฐ ์ฒ๋ฆฌ ์ค์ธ ์์ ์ JVM์ด ์ข ๋ฃ๋๋ฉด์ ์์ ์์ค์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
์์ ์ฝ๋: ForkJoinPool ์ ExecutorService ๋น๊ต
์๋ ์ฝ๋๋ CompletableFuture๋ฅผ ํ์ฉํด ๋น๋๊ธฐ ์์
์ ์ฒ๋ฆฌํ๋ ์์ ์
๋๋ค.
๋น๋๊ธฐ ์์
์ ๊ธฐ๋ณธ์ ์ผ๋ก ForkJoinPool.commonPool()์ ๋ฐ๋ชฌ ์ค๋ ๋์์ ์คํ๋ฉ๋๋ค.
์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ฉ์ธ ์ฐ๋ ๋์์ CompletableFuture ๋ฅผ ํ์ฉํด doAsyncWithForkJoinPool ๋ฉ์๋๋ฅผ ๋น๋๊ธฐ๋ก ํธ์ถํ๊ณ ์์ต๋๋ค. ์ด ๋, ForkJoinPool.commonPool() ์ ๋ฐ๋ชฌ์ฐ๋ ๋๊ฐ doAsyncWithForkJoinPool ๋ฉ์๋๋ฅผ ์คํํฉ๋๋ค.
// Main ํด๋์ค
public static void main(String[] args) {
Task task = new Task();
CompletableFuture<Void> future = task.doAsyncWithForkJoinPool().thenAccept(s -> {
System.out.println("[main] Thread.currentThread().getName() = "
+ Thread.currentThread().getName());
System.out.println(s);
});
System.out.println("๋ฉ์ธ ์ข
๋ฃ");
}
// Task ํด๋์ค
public CompletableFuture<String> doAsyncWithForkJoinPool() {
return CompletableFuture.supplyAsync(() -> {
System.out.println("[doAsync] Thread.currentThread().getName() = "
+ Thread.currentThread().getName());
return "doAsync";
});
}
๊ฒฐ๊ณผ๋ '๋ฉ์ธ ์ข ๋ฃ' ๊ฐ ๋์์ต๋๋ค.
๋น๋๊ธฐ ์์ ์ ์คํํ๋ ๋ฐ๋ชฌ ์ค๋ ๋์์์ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋์ง ์์ต๋๋ค. ์ด๋ ๋ฉ์ธ ์ค๋ ๋๊ฐ ๋น ๋ฅด๊ฒ ์ข ๋ฃ๋๋ฉด์ JVM๋ ํจ๊ป ์ข ๋ฃ๋์๊ธฐ ๋๋ฌธ์ ๋๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ: ์ฌ์ฉ์ ์ค๋ ๋ ์ฌ์ฉ
๋ฐ๋ชฌ์ฐ๋ ๋๋ฅผ ์ฌ์ฉํ๋ ForkJoinPool ๋์ ์ฌ์ฉ์์ฐ๋ ๋๋ฅผ ์ฌ์ฉํ๋ ExecutorService๋ก ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํ๋ฉด, ๋น๋๊ธฐ ์์ ์ด ์๋ฃ๋ ๋๊น์ง JVM์ด ์ข ๋ฃ๋์ง ์์ต๋๋ค.
// Main ํด๋์ค
public static void main(String[] args) {
Task task = new Task();
CompletableFuture<Void> future = task.doAsyncWithExecutorService().thenAccept(s -> {
System.out.println("[main] Thread.currentThread().getName() = "
+ Thread.currentThread().getName());
System.out.println(s);
});
System.out.println("๋ฉ์ธ ์ข
๋ฃ");
}
// Task ํด๋์ค
public CompletableFuture<String> doAsyncWithExecutorService() {
ExecutorService es = Executors.newCachedThreadPool();
return CompletableFuture.supplyAsync(() -> {
System.out.println("[doAsync] Thread.currentThread().getName() = "
+ Thread.currentThread().getName());
return "doAsync";
}, es);
}
๊ฒฐ๊ณผ๋ ์ด 4์ค์ ๋ก๊ทธ๊ฐ ๋จ์์ต๋๋ค.
๋ฉ์ธ ์ข ๋ฃ
[doAsync] Thread.currentThread().getName() = pool-2-thread-1
[main] Thread.currentThread().getName() = pool-2-thread-1
doAsync
์ฆ, ExecutorService๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ์ฉ์ ์ค๋ ๋๊ฐ ๋น๋๊ธฐ ์์ ์ ์คํํ๋ฉฐ, JVM์ ๋ชจ๋ ์ฌ์ฉ์ ์ค๋ ๋๊ฐ ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํ ๋๊น์ง ์ข ๋ฃํ์ง ์๊ณ ๋๊ธฐํฉ๋๋ค. ์ด๋ก ์ธํด ๋น๋๊ธฐ ์์ ์ ๋ชจ๋ ๋ก๊ทธ๊ฐ ์ ์์ ์ผ๋ก ์ถ๋ ฅ๋ฉ๋๋ค.
๊ฒฐ๋ก ๋ฐ ๊ฐ์ธ์ ์ธ ์๊ฒฌ
์ฌ๊ธฐ์๋ถํฐ๋ ์ด ๊ฒฝํ์ ํตํด ์๊ฐํ๊ฒ ๋ ์ ๊ฐ์ธ์ ์ธ ์๊ฒฌ์ ๋๋ค.
๋น๋๊ธฐ๋ฅผ ์ฌ์ฉํ ๋๋ ์ฃผ๋ก I/O ์์
๊ณผ ๊ฐ์ด ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ฌ์ฉํฉ๋๋ค.
์ด๋ฌํ ์์
์ ForkJoinPool.commonPool()์ ์ฌ์ฉํ๋ CompletableFuture๋ก ์ฒ๋ฆฌํ๋ค๋ฉด, JVM์ด ์ ์์ ์ผ๋ก ์ข
๋ฃ๋ ๋ ์์
์ด ์๋ฃ๋์ง ์์์์๋ JVM์ด ์ข
๋ฃ๋๋ฉด์ ์์
์์ค์ด ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ์์ต๋๋ค.
์ค๋ ๋ ํ์ ํ์ตํ๋ค ๋ณด๋ฉด ์ฐ์ํ ์ข
๋ฃ(Graceful Shutdown)๋ผ๋ ๊ฐ๋
์ ์ ํ๊ฒ ๋ฉ๋๋ค.
์ฐ์ํ ์ข
๋ฃ๋, ํ์ ๋จ์ ์๋ ์์
์ ๋ชจ๋ ์ฒ๋ฆฌํ ํ ์ข
๋ฃํ์ฌ ์์
์์ค์ ๋ฐฉ์งํ๋ ๋ฐฉ์์
๋๋ค.
์ด๋ ์์
์ ์ ๋ขฐ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํ ํ์์ ์ธ ์ต๊ด์ผ๋ก, ๋ชจ๋ ์๋ฐ ์ธ ์ก์
์์๋ ์ด๋ฌํ ์ค์์ฑ์ ๊ฐ์กฐํ๊ณ ์์๋๋ฐ์.
์ด์ ๋ง์ฐฌ๊ฐ์ง๋ก, CompletableFuture ์์
๋ ์ฐ์ํ ์ข
๋ฃ์ฒ๋ผ ์์
์์ค์ด ์์ด์ผ ํ์ง ์์๊น? ๋ผ๊ณ ์๊ฐํด๋ด
๋๋ค.
๋ฐ๋ผ์, ๋ฐ๋ชฌ ์ค๋ ๋๋ฅผ ์์ปค ์ค๋ ๋๋ก ์ฌ์ฉํ๋ ForkJoinPool ๋์ , ExecutorService ๊ธฐ๋ฐ์ ์ฌ์ฉ์ ์ค๋ ๋ ํ์ ์ฌ์ฉํด์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์ ์์ ์ผ๋ก ์ข
๋ฃ ๋ ์ ์๊ฒ ํ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค๋ ๊ฒฐ๋ก ์ ๋ง์๋๋ฆฌ๋ฉฐ ๊ธ์ ๋ง๋ฌด๋ฆฌ ํ๊ฒ ์ต๋๋ค.
์ฐธ๊ณ
- ์คํ์ค๋ฒํ๋ก์ฐ: Why is CompletableFuture.supplyAsync succeeding a random number of times?
'Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Java์ ๋๊ธฐํ ๊ธฐ๋ฒ - synchronized & ReentrantLock ์ ๋ํด (0) | 2024.12.30 |
---|---|
ํต์ฌ ๊ธฐ๋ฅ์ ๋จผ์ ๊ตฌํํ๊ธฐ ์ํ ๊ณ ๋ฏผ๊ณผ ๋ ธ๋ ฅ (3) | 2023.11.27 |