ExecutorService / Executors.newFixedThreadPool()
| 対応: | Java 5(2004) |
|---|
スレッドプールを使って複数のタスクを効率よく並行実行する仕組みです(Java 5以降)。スレッドの生成・再利用・終了を自動管理するため、『Thread』を直接使うより安全でパフォーマンスに優れています。
構文
import java.util.concurrent.*; // 固定サイズのスレッドプールを作成します ExecutorService exec = Executors.newFixedThreadPool(スレッド数); // 単一スレッドのExecutorを作成します ExecutorService exec = Executors.newSingleThreadExecutor(); // 必要に応じてスレッドを増減するプールを作成します ExecutorService exec = Executors.newCachedThreadPool(); // Runnableタスクを送信します(戻り値なし) exec.execute(Runnable task); // Callableタスクを送信して Future を受け取ります(戻り値あり) Future<T> future = exec.submit(Callable<T> task); T result = future.get(); // ExecutorServiceをシャットダウンします(必須) exec.shutdown(); exec.shutdownNow();
主なメソッド一覧
| メソッド | 概要 |
|---|---|
| Executors.newFixedThreadPool(n) | n 個のスレッドからなる固定サイズのプールを作成します。 |
| Executors.newSingleThreadExecutor() | 1つのスレッドでタスクをキューに積んで順に実行します。 |
| Executors.newCachedThreadPool() | 必要に応じてスレッドを生成・再利用します。短命のタスクに適しています。 |
| execute(Runnable) | タスクを非同期で実行します。戻り値はありません。 |
| submit(Callable<T>) | タスクを非同期で実行し、結果を Future<T> で返します。 |
| future.get() | タスクの結果が返るまでブロックして待機します。 |
| shutdown() | 新しいタスクの受け付けを停止し、既存タスクの完了を待って終了します。 |
| shutdownNow() | 実行中のタスクを中断してすぐにシャットダウンします。 |
| awaitTermination(timeout, unit) | 指定時間内に終了するまで待機します。 |
サンプルコード
sample_ExecutorServiceExample.java
import java.util.concurrent.*;
class ExecutorServiceExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 3スレッドのプールで5つのタスクを並行実行します
ExecutorService exec = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 5; i++) {
final int taskId = i;
exec.execute(() -> {
System.out.println("タスク" + taskId + " を実行中: " + Thread.currentThread().getName());
try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
System.out.println("タスク" + taskId + " が完了しました。");
});
}
// 新しいタスクの受け付けを停止し、残りのタスクが完了するまで待ちます
exec.shutdown();
// submit() で戻り値を持つタスクを実行します
ExecutorService exec2 = Executors.newFixedThreadPool(2);
Future<Integer> future1 = exec2.submit(() -> { Thread.sleep(200); return 10 + 20; });
Future<Integer> future2 = exec2.submit(() -> { Thread.sleep(100); return 30 + 40; });
try {
System.out.println("結果1: " + future1.get()); // 『結果1: 30』と出力されます。
System.out.println("結果2: " + future2.get()); // 『結果2: 70』と出力されます。
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
exec2.shutdown();
}
}
}
ExecutorServiceExample.java
javac ExecutorServiceExample.java java ExecutorServiceExample タスク1 を実行中: pool-1-thread-1 タスク2 を実行中: pool-1-thread-2 タスク3 を実行中: pool-1-thread-3 タスク1 が完了しました。 タスク4 を実行中: pool-1-thread-1 タスク2 が完了しました。 タスク5 を実行中: pool-1-thread-2 タスク3 が完了しました。 タスク4 が完了しました。 タスク5 が完了しました。 結果1: 30 結果2: 70
※ スレッドの実行順序は実行環境によって変わります。
概要
スレッドプールを使うことでスレッドの生成コストを削減し、アプリケーションのスループットを向上できます。Executors.newFixedThreadPool() はCPUコア数に合わせたスレッド数を指定するのが一般的で、IOバウンド(ファイル読み書きやネットワーク通信など、I/O待ちが処理時間の大部分を占める状態)な処理ではコア数より多いスレッドを使うのが効果的です。
『shutdown()』を呼ばないとプログラムが終了せずに残り続けることがあります。try-finally を使って必ず呼び出すようにしてください。より高度な非同期処理には『CompletableFuture』を使うことで、複数の非同期タスクを組み合わせて扱えます。
スレッドの基本については『new Thread() / スレッド.start() / Runnable』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。