Javaにおける非同期タスクのスケジューリング実装

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 は前回終了後の遅延を保証。

タグ: Java ScheduledExecutorService 非同期処理

7月6日 00:46 投稿