Executorフレームワークはスレッドのプールを管理し、手動でスレッドを作成する代わりにタスクを送信でき、**CompletableFuture**は非同期操作を宣言的に組み合わせます。これらを組み合わせることで、Javaで並行/非同期処理を行う現代的な方法となり、生のスレッド管理の費用と複雑性を避けることができます。
生のスレッドの問題点
(Task t : tasks) {
(() -> process(t)).start();
}
// ✅ a fixed pool reuses a bounded set of threads for many tasks
ExecutorService executor = Executors.newFixedThreadPool(10);
Future<Integer> future = executor.submit(() -> compute()); // submit a task
Integer result = future.get(); // blocks until the result is ready
executor.shutdown(); // stop accepting tasks; let running ones finish
ExecutorServiceは何を実行するか(タスク)とどのように実行するか(スレッド管理)を分離し、限定されたスレッドプールを再利用してリソース枯渇を防ぎ、スレッド生成の継続的なオーバーヘッドを減らします。
Future<Integer> f = executor.submit(task);
f.get(); // ❌ BLOCKS the calling thread; can't chain/combine without blocking
Future.get()はブロックし、依存する操作を簡単にチェーンしたり結果を組み合わせたりできません。これがCompletableFutureが修正するところです。
CompletableFuture.supplyAsync(() -> fetchUser(id)) // run async, returns a value
.thenApply(user -> user.getName()) // transform the result (non-blocking)
.thenApply(String::toUpperCase)
.thenAccept(name -> System.out.println(name)) // consume the final result
.exceptionally(ex -> { log(ex); return null; }); // handle errors in the chain
CompletableFutureを使うことで、ノンブロッキングなパイプラインを構築でき、変換(thenApply)、依存する非同期呼び出しのチェーン(thenCompose)、エラーハンドリング(exceptionally)ができ、スレッドをブロックすることはありません。
// run two independent async calls in PARALLEL and combine their results
CompletableFuture<User> userF = CompletableFuture.supplyAsync(() -> fetchUser(id));
CompletableFuture<Order> orderF = CompletableFuture.supplyAsync(() -> fetchOrder(id));
userF.thenCombine(orderF, (user, order) -> buildProfile(user, order))
.thenAccept(profile -> render(profile));
// wait for many to complete
CompletableFuture.allOf(f1, f2, f3).join(); // all done
thenCombine、allOf、anyOfは複数の非同期タスクを優雅に調整し、独立した処理を並列実行して結果を組み合わせます。
ExectorフレームワークとCompletableFutureは、エラーが起きやすい手動スレッド管理に代わり、Javaで並行性と非同期処理を扱う現代的で正しい方法です。
スレッドプール(ExecutorService)は、タスクごとにスレッドを作成するリソース枯渇とオーバーヘッドを避けます。これはスケーラブルなサーバーに不可欠です。
CompletableFutureはFutureのブロッキングの制限を解決し、組み合わせ可能でノンブロッキングなパイプラインを実現します。チェーン、変換、組み合わせ、非同期操作のエラーハンドリングが宣言的にできます。これはデータベース、API、マイクロサービスなど多くの並行呼び出しを調整する必要があるI/O境界のサービスで重要です。
生のスレッドとブロッキングFutureではなくこれらを理解することは、パフォーマンスとスケーラビリティに優れた並行Java アプリケーションを構築するうえで基本的なことであり、シニアレベルの重要な能力です。