Javaでは、ScheduledExecutorService を用いて非同期で遅延または周期的なタスクを実行できる。これは標準ライブラリが提供するスケジューリング専用のスレッドプールインタフェースであり、柔軟な制御が可能である。
基本的な使用方法
ScheduledThreadPoolExecutor クラスはこのインタフェースの具象実装であり、直接インスタンス化することも、Executors.newScheduledThreadPool(n) などのファクトリメソッド経由で生成することもできる。
主要なスケジューリングメソッド
| メソッド名 | 動作概要 |
|---|---|
schedule(Runnable, long, TimeUnit) | 指定遅延後に1回だけ実行 |
schedule(Callable<V>, long, TimeUnit) | 遅延後実行し結果を返す |
scheduleAtFixedRate(...) | 固定間隔で繰り返し(前回終了に関わらず) |
scheduleWithFixedDelay(...) | 前回終了から固定遅延後に次を開始 |
サンプルコード集
単発遅延タスク
import java.util.concurrent.*;
public class OneShotScheduler {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
Runnable job = () ->
System.out.println("処理時刻: " + (System.currentTimeMillis() / 1000));
System.out.println("開始時刻: " + (System.currentTimeMillis() / 1000));
pool.schedule(job, 3, TimeUnit.SECONDS);
// タスク完了を待ってシャットダウン
pool.shutdown();
try {
if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
pool.shutdownNow();
}
} catch (InterruptedException e) {
pool.shutdownNow();
}
}
}
結果を返す遅延タスク
import java.util.concurrent.*;
public class CallableScheduler {
public static void main(String[] args) throws Exception {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
Callable<String> worker = () -> {
Thread.sleep(1000);
return "計算結果";
};
ScheduledFuture<String> result = pool.schedule(worker, 2, TimeUnit.SECONDS);
System.out.println("取得結果: " + result.get()); // 実行完了までブロック
pool.shutdown();
}
}
固定間隔での周期実行
import java.util.concurrent.*;
public class RateBasedScheduler {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
Runnable recurring = () -> {
long start = System.currentTimeMillis() / 1000;
System.out.println("開始: " + start);
try { Thread.sleep(2000); }
catch (InterruptedException ignored) {}
};
// 初回1秒後、以降3秒ごとに実行(重複実行あり)
pool.scheduleAtFixedRate(recurring, 1, 3, TimeUnit.SECONDS);
// 10秒後に自動終了
pool.schedule(pool::shutdown, 10, TimeUnit.SECONDS);
}
}
終了後遅延での周期実行
import java.util.concurrent.*;
public class DelayBasedScheduler {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
Runnable sequential = () -> {
long enter = System.currentTimeMillis() / 1000;
System.out.println("開始: " + enter);
try { Thread.sleep(2000); }
catch (InterruptedException ignored) {}
System.out.println("終了: " + (System.currentTimeMillis() / 1000));
};
// 前回終了から3秒後に次を開始
pool.scheduleWithFixedDelay(sequential, 1, 3, TimeUnit.SECONDS);
pool.schedule(() -> {
pool.shutdown();
System.out.println("スケジューラ停止");
}, 15, TimeUnit.SECONDS);
}
}
重要な設計上の考慮点
- スレッド数調整:並列実行が必要な場合はスレッド数を増やす。順次実行なら
newSingleThreadScheduledExecutor()を利用。 - 例外ハンドリング:未捕捉例外でスケジューリングが止まる可能性があるため、内部でtry-catchを設ける。
- リソース解放:
shutdown()とawaitTermination()の組み合わせで安全に終了させる。 - 実行間隔の違い:
scheduleAtFixedRateは時間通りに起動(重複可)、scheduleWithFixedDelayは前回終了後の遅延を保証。