Java上級

上級 Javaで学ぶ非同期処理|練習問題編

導入

非同期処理は、現代のアプリケーション開発において不可欠な技術です。特に、ネットワーク通信やデータベースアクセスなど、時間のかかる処理を効率的に行うためには、非同期処理を活用することが重要です。Javaでは、CompletableFutureやExecutorServiceなどの機能を使用することで、非同期処理を効果的に実装できます。本記事では、具体的なシチュエーションを通じて、非同期処理の実践的な利用方法を学びます。

教科書レベルの解説(非同期処理)

重要な概念の整理

非同期処理は、メインスレッドをブロックすることなく、別のスレッドで処理を実行することを意味します。これにより、ユーザーインターフェースの応答性を保ちながら、バックグラウンドでデータを取得したり、計算を行ったりすることが可能になります。Javaでは、非同期処理を実現するために、以下のような技術が利用されます。

  • ExecutorService: スレッドプールを管理し、非同期タスクを実行するためのインターフェース。
  • CompletableFuture: 非同期処理の結果を扱うための強力なAPIで、チェーンメソッドによる柔軟な処理が可能。
  • Future: 非同期処理の結果を表すオブジェクトで、処理が完了するまで待機することができる。

コード例(Java)


import java.util.concurrent.CompletableFuture;

public class AsyncExample {
    public static void main(String[] args) {
        CompletableFuture future = CompletableFuture.runAsync(() -> {
            try {
                // 模擬的な長時間処理
                Thread.sleep(2000);
                System.out.println("非同期処理が完了しました。");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 他の処理をここで行う
        System.out.println("メインスレッドでの処理中...");
        
        // 非同期処理の完了を待つ
        future.join();
        System.out.println("メインスレッドの処理が終了しました。");
    }
}

コードの行ごとの解説

  1. CompletableFutureを使用して非同期処理を開始します。
  2. runAsyncメソッドにより、別スレッドで処理を実行します。
  3. Thread.sleep()を用いて、模擬的な長時間処理を行います。
  4. メインスレッドは、非同期処理が完了するまで他の処理を続行します。
  5. future.join()により、非同期処理の完了を待ちます。
  6. 全ての処理が完了したら、メインスレッドの終了メッセージを表示します。

練習問題編

以下の練習問題に取り組んで、非同期処理の理解を深めましょう。

  1. 問題1: CompletableFutureを使用して、2つの非同期処理を実行し、両方の結果を統合するコードを書いてください。
  2. 問題2: ExecutorServiceを利用して、複数のタスクを並行して実行するプログラムを作成してください。
  3. 問題3: 非同期処理のエラーハンドリングを考慮したCompletableFutureの実装方法を説明してください。

模範解答

問題1の解答:


CompletableFuture future1 = CompletableFuture.supplyAsync(() -> 5);
CompletableFuture future2 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture combinedFuture = future1.thenCombine(future2, Integer::sum);
combinedFuture.thenAccept(result -> System.out.println("合計: " + result));

問題2の解答:


ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(() -> { /* タスク1の処理 */ });
executorService.submit(() -> { /* タスク2の処理 */ });
executorService.shutdown();

問題3の解答:


CompletableFuture.supplyAsync(() -> { /* 処理 */ })
    .exceptionally(ex -> {
        System.out.println("エラーが発生しました: " + ex.getMessage());
        return null;
    });

まとめ

  • 非同期処理を適切に活用することで、アプリケーションのパフォーマンスを向上させることができる。
  • CompletableFutureやExecutorServiceを使いこなすことで、複雑な非同期処理も簡潔に実装できる。
  • エラーハンドリングを考慮することで、より堅牢な非同期処理を実現できる。