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

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

---

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

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

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

---

### **2. Executorインターフェース**

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

```java
public interface Executor {
    void execute(Runnable command);
}
```

- **使い方**:
  - `execute`メソッドに`Runnable`を渡してタスクを非同期実行します。渡す`Runnable`はラムダ式で記述してもOKです。

#### **サンプルコード**
```java:
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"));
    }
}
```
```plaintext:実行結果
Task executed
```

---

### **3. ExecutorServiceインターフェース**

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

#### **主なメソッド**

1. **`submit()`**
   - `Runnable`または`Callable`タスクをスレッドプールに提出します。
   - `Future`オブジェクトを返す。

2. **`shutdown()`**
   - スレッドプールの正常終了を指示します。

3. **`shutdownNow()`**
   - 実行中のタスクを強制終了します。

4. **`invokeAll()`**
   - 複数のタスクを同時に実行し、すべて完了するまで待機。

5. **`invokeAny()`**
   - 複数のタスクのうち、最初に完了したタスクの結果を返します。

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

#### **サンプルコード: `submit`と`shutdown`の使用例**
```java
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();
    }
}
```

```plaintext:実行結果
Task 1 executed
Task 2 executed
```
---

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

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

#### **主なメソッド**

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

2. **`cancel(boolean mayInterruptIfRunning)`**
   - タスクをキャンセルします。
   - 実行中のタスクを中断する場合は`true`を指定。

3. **`isDone()`**
   - タスクが完了しているかどうかを返します。

4. **`isCancelled()`**
   - タスクがキャンセルされたかどうかを返します。

#### **サンプルコード: タスクの実行とキャンセル**
```java
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();
        }
    }
}
```

```plaintext:実行結果
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)`**
   - 指定した初期遅延後、各タスクの終了時から一定時間遅延して次のタスクを実行します。

---

#### **サンプルコード: 周期的タスクの実行**
```java
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();
        }
    }
}
```

```plaintext:実行結果
Task executed at 1647250800000
Task executed at 1647250803000
Task executed at 1647250806000
Task executed at 1647250809000
```
---
### **6. Executorsユーティリティクラス**

**`Executors`** は`Executor`、`ExecutorService`、`ScheduledExecutorService`インターフェースを実装した具体的なクラス（例: `ThreadPoolExecutor`、`ScheduledThreadPoolExecutor`）を簡単に生成するためのユーティリティクラスです。

#### **主なメソッド**

1. **`newFixedThreadPool(int nThreads)`**
   - 固定サイズのスレッドプールを作成。

2. **`newCachedThreadPool()`**
   - 必要に応じてスレッドを生成し、再利用するスレッドプールを作成。

3. **`newSingleThreadExecutor()`**
   - シングルスレッドのExecutorを作成。

4. **`newScheduledThreadPool(int corePoolSize)`**
   - スケジュール実行可能なスレッドプールを作成。

#### **サンプルコード: `newFixedThreadPool`の使用例**
```java
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();
    }
}
```

```plaintext:実行結果
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`は、スレッドプールを詳細にカスタマイズするためのクラスです。

#### **主なコンストラクタ**
```java
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
    TimeUnit unit, BlockingQueue<Runnable> workQueue)
```

- **`corePoolSize`**: プール内の基本スレッド数。
- **`maximumPoolSize`**: プール内の最大スレッド数。
- **`keepAliveTime`**: アイドル状態のスレッドをプールに保持する時間。
- **`workQueue`**: タスクを保持するキュー。

#### **サンプルコード: カスタムスレッドプール**
```java:
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();
    }
}
```

```plaintext:実行結果
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`** ユーティリティクラスによって`Executor`、`ExecutorService`、`ScheduledExecutorService`インターフェースを実装したクラスのインスタンスを簡単に生成できます。
- **`ThreadPoolExecutor`** によってより高度な制御が可能になります。