ExecutorService とスレッドプールの種類
固定、キャッシュ、単一、スケジュール実行のスレッドプールを作成し、それぞれの使いどころを理解します。
「ExecutorService とスレッドプールの種類」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
スレッドプールを使う理由
タスクごとに新しいThreadを作成するのはコストがかかります。スレッドの作成と破棄にはオーバーヘッドがあるためです。スレッドプールは一定数のスレッドを再利用して多数のタスクを実行し、オーバーヘッドを削減するとともにリソース消費を制御します。
import java.util.concurrent.*;
// Bad practice: new thread per task
new Thread(() -> System.out.println("task")).start();
// Better: reuse threads from a pool
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("task from pool"));
pool.shutdown();newFixedThreadPool
正確にN個のスレッドを持つプールを作成します。余分なタスクは、スレッドが利用可能になるまでキューに入ります。必要な並列度が分かっている場合に適しています。
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
final int taskId = i;
pool.submit(() -> {
System.out.println("Task " + taskId + " on " + Thread.currentThread().getName());
});
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);newCachedThreadPool
必要に応じてスレッドを作成し、アイドル状態のスレッドを再利用します。60秒間アイドル状態のスレッドは終了します。短時間で終わるタスクが多数ある場合に適していますが、長時間実行するタスクや無制限のタスクには危険です(メモリを使い果たす可能性があります)。
ExecutorService cached = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
cached.submit(() -> {
// quick task
double result = Math.sqrt(Math.random());
});
}
cached.shutdown();newSingleThreadExecutor
スレッドを正確に1つだけ持つプールです。タスクは送信された順序で逐次実行されます。共有リソースへのアクセスを直列化する場合に適しています。
ExecutorService single = Executors.newSingleThreadExecutor();
single.submit(() -> System.out.println("Task 1"));
single.submit(() -> System.out.println("Task 2"));
single.submit(() -> System.out.println("Task 3"));
// Always prints: Task 1, Task 2, Task 3 (sequential)
single.shutdown();newScheduledThreadPool
スケジュール実行と繰り返し実行のタスクをサポートします。ポーリング、リトライ、バックグラウンドの保守処理に使用します。
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// One-shot delay
scheduler.schedule(() -> System.out.println("Delayed!"),
3, TimeUnit.SECONDS);
// Fixed rate (every 2 seconds)
scheduler.scheduleAtFixedRate(
() -> System.out.println("Tick"),
0, 2, TimeUnit.SECONDS);
// Run later: scheduler.shutdown();VirtualThreadExecutor(Java 21以降)
Java 21では、軽量で作成コストの低い仮想スレッドが導入されました。I/O負荷の高いワークロードにはExecutors.newVirtualThreadPerTaskExecutor()を使用します。
// Java 21+
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
final int id = i;
exec.submit(() -> {
Thread.sleep(100); // blocks without tying up OS thread
System.out.println("vthread " + id);
});
}
} // auto-shutdown適切なプールの選択
ガイドライン:
- CPUバウンドタスク:NをCPUコア数とした固定プール
- I/Oバウンドタスク:キャッシュプールまたは仮想スレッド
- 逐次実行タスク:シングルスレッドエグゼキューター
- スケジュール実行・繰り返し実行:スケジュールスレッドプール
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService cpuPool = Executors.newFixedThreadPool(cores);
System.out.println("Pool size: " + cores);カスタムThreadPoolExecutor
細かく制御するには、ThreadPoolExecutorを直接作成します。
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
2, // corePoolSize
10, // maximumPoolSize
60L, // keepAliveTime
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100), // work queue
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);
tpe.submit(() -> System.out.println("Custom pool task"));
tpe.shutdown();正常なシャットダウン
スレッドを解放するため、必ずExecutorServiceをシャットダウンしてください。shutdown()は新しいタスクの受け付けを停止しますが、キューに入っているタスクは完了させます。shutdownNow()は実行中のタスクのキャンセルを試みます。
ExecutorService pool = Executors.newFixedThreadPool(4);
// ... submit tasks ...
pool.shutdown(); // no new tasks
try {
if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
pool.shutdownNow(); // force cancel
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}AutoCloseableとしてのExecutorService(Java 19以降)
Java 19以降、ExecutorServiceはAutoCloseableを実装しています。自動的にシャットダウンするには、try-with-resourcesを使用します。
// Java 19+ (preview), Java 21 stable
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
pool.submit(() -> System.out.println("task"));
} // pool.close() called: waits for tasks then shuts down拒否ポリシー
プールが飽和状態(すべてのスレッドが使用中で、キューも満杯)になると、拒否ポリシーが超過したタスクを処理します。
- AbortPolicy(デフォルト):
RejectedExecutionExceptionをスローします - CallerRunsPolicy:呼び出し元のスレッドでタスクを実行します
- DiscardPolicy:タスクを通知なしに破棄します
- DiscardOldestPolicy:キュー内で最も古いタスクを破棄します
確認問題
重い計算を実行するCPUバウンドタスクがあります。固定スレッドプールには何個のスレッドを用意すべきですか?
復習:ExecutorServiceとスレッドプール
重要なポイント:
- 固定プール:スレッド数Nが固定され、余分なタスクはキューに入ります
- キャッシュプール:必要に応じて拡張し、アイドル状態のスレッドは60秒後に破棄されます
- シングルスレッド:逐次実行の順序が保証されます
- スケジュールプール:遅延タスクと繰り返しタスクに使用します
- 必ずshutdown()を呼び出し、正常に終了するにはawaitTermination()を使用します
AI チューターと学ぶ Java — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 104
- レッスン
- 374
よくある質問
「ExecutorService とスレッドプールの種類」レッスンは無料ですか?
はい。「ExecutorService とスレッドプールの種類」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「ExecutorService とスレッドプールの種類」で何を学びますか?
固定、キャッシュ、単一、スケジュール実行のスレッドプールを作成し、それぞれの使いどころを理解します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「ExecutorService とスレッドプールの種類」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ExecutorService とスレッドプールの種類
- タスクの送信:Runnable と Callable
- Future とエラー処理
- 定期タスクのための ScheduledExecutorService