2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java Gold】Executorフレームワーク完全に理解した

Last updated at Posted at 2024-12-28

Javaの並列処理において、Executorフレームワークは非常に重要な役割を果たします。
似たようなインタフェースやらメソッドやらが多くてごちゃごちゃしてたExecutorフレームワークですが、この度完全に理解したのでまとめます。


1. Executorフレームワークの概要

Executorフレームワークは、スレッドの直接管理を避け、タスク実行を簡素化するための仕組みを提供します。スレッドプールを活用することで、スレッドの再利用やリソース管理を効率化します。

主な利点

  • スレッド管理の簡略化: 手動でスレッドを作成・破棄する必要なし!
  • リソース効率の向上: スレッドプールの利用でスレッドを再利用!
  • 高度なタスク管理: スケジュール実行や非同期処理の管理が簡単!

2. Executorインターフェース

定義

Executorは基本的なタスク実行のためのインターフェースです。

public interface Executor {
    void execute(Runnable command);
}
  • 使い方:
    • executeメソッドにRunnableを渡してタスクを非同期実行します。渡すRunnableはラムダ式で記述してもOKです。

サンプルコード

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class ExecutorExample {
    public static void main(String[] args) {
        // ユーティリティクラスのExecutors(後述)でスレッドを生成
        Executor executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> System.out.println("Task executed"));
    }
}
実行結果
Task executed

3. ExecutorServiceインターフェース

ExecutorServiceExecutorを拡張し、タスクの管理やスレッドプール操作を可能にするインターフェースです。

主なメソッド

  1. submit()

    • RunnableまたはCallableタスクをスレッドプールに提出します。
    • Futureオブジェクトを返す。
  2. shutdown()

    • スレッドプールの正常終了を指示します。
  3. shutdownNow()

    • 実行中のタスクを強制終了します。
  4. invokeAll()

    • 複数のタスクを同時に実行し、すべて完了するまで待機。
  5. invokeAny()

    • 複数のタスクのうち、最初に完了したタスクの結果を返します。

もちろん、execute()も持っています。

サンプルコード: submitshutdownの使用例

import java.util.concurrent.*;

public class ExecutorServiceExample {
    public static void main(String[] args) {

        // ユーティリティクラスのExecutors(後述)でスレッドを生成
        ExecutorService executor = Executors.newFixedThreadPool(2);

        Runnable task1 = () -> System.out.println("Task 1 executed");
        Runnable task2 = () -> System.out.println("Task 2 executed");

        executor.submit(task1);
        executor.submit(task2);

        executor.shutdown();
    }
}
実行結果
Task 1 executed
Task 2 executed

4. FutureインターフェースとCallableインターフェース

FutureCallableは非同期タスクを管理するためのインターフェースです。

  • Callable: タスクを記述するためのインターフェース。Runnableと異なり、戻り値を返すことができます。call()を実装します。
  • Future: 非同期タスクの結果を取得したり、タスクのキャンセルを行うためのインターフェース。

主なメソッド

  1. get()

    • タスクの結果を取得します。
    • タスクが完了するまでブロッキングします。
    • 引数に(int time, TimeUnit)を渡すことで、その時間内にスレッドの処理が完了しなかった場合、TimeoutExecptionをスローします。
  2. cancel(boolean mayInterruptIfRunning)

    • タスクをキャンセルします。
    • 実行中のタスクを中断する場合はtrueを指定。
  3. isDone()

    • タスクが完了しているかどうかを返します。
  4. isCancelled()

    • タスクがキャンセルされたかどうかを返します。

サンプルコード: タスクの実行とキャンセル

import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) {
        // ユーティリティクラスのExecutors(後述)でスレッドを生成
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // taskをRunnableではなくCallableで定義
        Callable<String> task = () -> {
            // 10秒待機
            Thread.sleep(10000);
            return "Task completed!";
        };

        // Futureで結果を受け取る
        Future<String> future = executor.submit(task);

        System.out.println("Task submitted.");

        try {
            // タスクが完了しているかチェック
            if (!future.isDone()) {
                System.out.println("Task is still running...");
            }

            // タスクの結果を取得 5秒以内に完了しなければ例外をスロー
            String result = future.get(5, TimeUnit.SECONDS);
            System.out.println("Result: " + result);
        } catch (TimeoutException e) {
            System.out.println("Task timed out. Cancelling...");
            future.cancel(true); // タスクをキャンセル
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}
実行結果
Task submitted.
Task is still running...
Task timed out. Cancelling...

5. ScheduledExecutorServiceインターフェース

ScheduledExecutorServiceは、一定の遅延後や周期的にタスクを実行するためのインターフェースです。タスクはRunnableまたはCallableとして渡すことができます。

主なメソッド

  1. schedule(Runnable command, long delay, TimeUnit unit)

    • 指定した遅延後にタスクを実行します。
    • 戻り値はScheduledFuture<?>null になりますが、ScheduledFuture<?> を通じてタスクの完了状態を管理できます。
  2. schedule(Callable<V> callable, long delay, TimeUnit unit)

    • 指定した遅延後にタスクを実行し、結果を返します。
    • 戻り値はScheduledFuture<V>
  3. scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

    • 指定した初期遅延後、固定間隔でタスクを繰り返し実行します。
  4. scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

    • 指定した初期遅延後、各タスクの終了時から一定時間遅延して次のタスクを実行します。

サンプルコード: 周期的タスクの実行

import java.util.concurrent.*;

public class ScheduledExecutorExample {
    public static void main(String[] args) {
        // ユーティリティクラスのExecutors(後述)でスレッドを生成
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        Runnable task = () -> System.out.println("Task executed at " + System.currentTimeMillis());

        scheduler.scheduleAtFixedRate(task, 1, 3, TimeUnit.SECONDS);

        try {
            Thread.sleep(10000); // 10秒間実行
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            scheduler.shutdown();
        }
    }
}
実行結果
Task executed at 1647250800000
Task executed at 1647250803000
Task executed at 1647250806000
Task executed at 1647250809000

6. Executorsユーティリティクラス

ExecutorsExecutorExecutorServiceScheduledExecutorServiceインターフェースを実装した具体的なクラス(例: ThreadPoolExecutorScheduledThreadPoolExecutor)を簡単に生成するためのユーティリティクラスです。

主なメソッド

  1. newFixedThreadPool(int nThreads)

    • 固定サイズのスレッドプールを作成。
  2. newCachedThreadPool()

    • 必要に応じてスレッドを生成し、再利用するスレッドプールを作成。
  3. newSingleThreadExecutor()

    • シングルスレッドのExecutorを作成。
  4. newScheduledThreadPool(int corePoolSize)

    • スケジュール実行可能なスレッドプールを作成。

サンプルコード: newFixedThreadPoolの使用例

import java.util.concurrent.*;

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 1; i <= 5; i++) {
            final int taskNumber = i;
            executor.execute(() -> {
                System.out.println("Task " + taskNumber + " executed by " + Thread.currentThread().getName());
            });
        }

        executor.shutdown();
    }
}
実行結果
Task 1 executed by pool-1-thread-1
Task 2 executed by pool-1-thread-2
Task 3 executed by pool-1-thread-3
Task 4 executed by pool-1-thread-1
Task 5 executed by pool-1-thread-2

7. ThreadPoolExecutorインターフェース

ThreadPoolExecutorは、スレッドプールを詳細にカスタマイズするためのクラスです。

主なコンストラクタ

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
    TimeUnit unit, BlockingQueue<Runnable> workQueue)
  • corePoolSize: プール内の基本スレッド数。
  • maximumPoolSize: プール内の最大スレッド数。
  • keepAliveTime: アイドル状態のスレッドをプールに保持する時間。
  • workQueue: タスクを保持するキュー。

サンプルコード: カスタムスレッドプール

import java.util.concurrent.*;

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(2)
        );

        for (int i = 1; i <= 6; i++) {
            final int taskNumber = i;
            executor.execute(() -> {
                System.out.println("Task " + taskNumber + " executed by " + Thread.currentThread().getName());
            });
        }

        executor.shutdown();
    }
}
実行結果
Task 1 executed by pool-1-thread-1
Task 2 executed by pool-1-thread-2
Task 3 executed by pool-1-thread-3
Task 4 executed by pool-1-thread-4
Task 5 executed by pool-1-thread-1
Task 6 executed by pool-1-thread-2

8. execute()submit()schedule()の整理

特徴 execute() submit() schedule()
引数 Runnable Runnable または Callable Runnable または Callable
戻り値 void Future ScheduledFuture
結果の取得 不可 Future を使用して取得可能 ScheduledFuture を使用して取得可能
例外の処理 スレッドプールに通知される Future.get()ExecutionExceptionとして取得可能 ScheduledFuture.get()ExecutionExceptionとして取得可能
遅延実行 不可 不可 指定した遅延時間後にタスクを実行可能
用途 単純な非同期タスクの実行 結果取得やタスクの完了状態の追跡が必要な場合 遅延実行またはスケジュールタスクを実行したい場合

9. まとめ

Executor
└── ExecutorService
    ├── ScheduledExecutorService
    └── AbstractExecutorService (抽象クラス)
        └── ThreadPoolExecutor (具体的実装)


Executors (ユーティリティクラス)
  • Executor フレームワークは、スレッド管理を簡略化し、効率的なタスク実行を可能にします。
  • ExecutorService によってスレッドプールを活用して非同期タスクを柔軟に管理できます。
  • ScheduledExecutorService によって周期的タスクや遅延タスクを実行できます。
  • Executors ユーティリティクラスによってExecutorExecutorServiceScheduledExecutorServiceインターフェースを実装したクラスのインスタンスを簡単に生成できます。
  • ThreadPoolExecutor によってより高度な制御が可能になります。
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?