本ページはプロモーションが含まれています

Java入門

Java Timerの使い方|scheduleの実例と移行手順

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

「JavaのTimerクラスで一定時間後・定期的に処理を実行したいが、schedulescheduleAtFixedRateの使い分けが分からない」「ググるとScheduledExecutorServiceを使えと出てくるけど結局どっちが正解?」。

この記事は、そんな疑問にコード例ベースで一気に答えるためのガイドです。

結論を先に言うと、新規実装ではScheduledExecutorServiceが推奨ですが、Timerはレガシーコードの保守やJava標準ライブラリだけで完結させたい場面で今も現役です。

本記事では2つの違いを踏まえつつ、java timerの使い方を実例コードで丁寧に解説します。

Javaを学習していると、タスクのスケジュール実行という壁にぶつかることがあります。

私自身、新人の頃に簡単なバッチ処理を任された際、このjava.util.Timerの扱いでかなり時間を溶かしてしまった苦い経験があります。

特に、例外が発生したときの予期せぬ挙動に頭を悩ませました。

この記事は、過去の私と同じようにJavaのTimerについて知りたい方へ向けて書いています。

この記事を読めば、以下の内容がわかります。

  • JavaのTimerクラスの基本的な仕組みと使い方
  • 具体的なコード例(遅延実行、繰り返し実行)
  • 実務でハマりがちな5つの注意点
  • よりモダンな代替技術との比較

java timerの基礎から実践的な注意点まで網羅しているので、読み終える頃には自信を持ってタスクのスケジュール実行を実装できるようになるでしょう。

JavaのTimerとは?

JavaのTimerクラスとは何かを示す図解

JavaにおけるTimerとは、指定した時間や間隔で特定のタスクを実行するためのクラスです。java.utilパッケージに含まれており、Javaの標準ライブラリの一部として提供されています。

例えば、以下のような処理を実現するのに役立ちます。

ポイント

  • アプリケーション起動の5秒後に初期化処理を行う(Spring Bootなら@PostConstruct@Scheduledでも代替可)
  • 毎日深夜0時にログファイルを整理する
  • 1分ごとにサーバーの死活監視を行う

バックグラウンドで定期的な処理や遅延実行を行いたい場合に便利な仕組みです。

Timerクラスの基本概要

Timerクラスは、タスクをスケジュールするためのタイマー本体(指揮者)です。

「何秒後に」「何分ごとに」といった指示を登録すると、指定のタイミングで処理を呼び出してくれます。

重要な特徴として、Timerは内部的に単一のバックグラウンドスレッドでタスクを管理します。

つまり、登録されたタスクがいくつあっても、それらは一つのスレッド上で順番に実行されるのです。

このシングルスレッドという性質は、Timerを扱う上で非常に重要なポイントになるので覚えておきましょう。

TimerとTimerTaskの関係

Timerを理解する上で欠かせないのがTimerTaskクラスです。この2つはセットで使われます。

  • Timer: いつタスクを実行するかを決めるスケジューラー本体
  • TimerTask: 実際に実行される処理内容を書く「タスク本体」(run()メソッドに処理を書く)

まずTimerTaskrunメソッドの中に実行したい処理を書きます。

そのTimerTaskのインスタンスをTimerscheduleメソッドなどに渡すことで、タスクのスケジュールが予約されます。

Timerの使い方

JavaのTimerクラスの使い方(scheduleとscheduleAtFixedRate)を示す図解

Timerの主な使い方は、単発でタスクを実行する方法と、定期的にタスクを実行する方法の2つに大別されます。

それぞれの代表的なメソッドを見ていきましょう。

単発タスクを実行する(schedule)

一度だけタスクを実行したい場合はschedule(TimerTask task, long delay)メソッドを使います。

このメソッドは、指定したdelay(ミリ秒)が経過した後に、引数で渡されたtaskを一度だけ実行します。

例えば、「10秒後にこの処理を実行する」といった場合に利用できます。

また、schedule(TimerTask task, Date time)という形式もあり、こちらは特定の日時を指定してタスクを予約できます。

「明日の朝8時に実行する」といった制御が可能です。

定期的にタスクを実行する(scheduleAtFixedRate)

タスクを一定間隔で繰り返し実行したい場合にはscheduleAtFixedRate(TimerTask task, long delay, long period)メソッドが便利です。

  • delay: 最初のタスクが実行されるまでの待機時間(ミリ秒)
  • period: 2回目以降のタスクを実行する間隔(ミリ秒)

このメソッドの大きな特徴は、タスクの開始時間を基準に次の実行スケジュールを決定する点です。

例えば、「10秒ごとに実行」と設定した場合、前のタスクの処理に3秒かかったとしても、次のタスクは7秒後に開始されます。

タスクの実行時間がperiodを超えた場合は、前のタスクが終わり次第、遅延していたタスクがすぐに実行されます。

正確な周期で処理を実行したい場合に適しています。

schedule(task, delay, period)で前タスクの終了時刻基準で繰り返す

もう一つ、定期実行のメソッドとしてschedule(TimerTask task, long delay, long period)があります。

scheduleAtFixedRateとよく似ていますが、こちらは前のタスクが終了した時間を基準に次の実行スケジュールを決定する点が異なります。

例えば、「10秒間隔」で設定した場合、あるタスクの処理に3秒かかったら、次のタスクが実行されるのはその10秒後です。

つまり、実際の実行間隔は「処理時間 + period」となります。

処理が遅延しても、前のタスクが完了してから一定時間後に次を実行したい、というシナリオで有効な方法です。

scheduleとscheduleAtFixedRateの使い分け早見表

3つのメソッドの違いを表で整理すると、目的に応じて素早く選択できます。

用途メソッド基準遅延時の挙動
1回だけ実行(遅延)schedule(task, delay)
1回だけ実行(特定日時)schedule(task, Date)
正確な周期で繰り返しscheduleAtFixedRate(task, delay, period)タスクの開始時刻遅延分を取り戻すため連続実行する
処理完了後に一定間隔で繰り返しschedule(task, delay, period)タスクの終了時刻毎回 終了+period 後に実行

負荷集中を避けたいならschedule系(終了時刻基準)、定刻処理を厳密に守りたいならscheduleAtFixedRateを選ぶのが基本方針です。

Timerを使ったコード例

JavaのTimerを使ったコード例を示す図解

具体的なコードでjava timerの使い方を見ていきましょう。3つのパターン(単発・繰り返し・遅延)を順に紹介します。

簡単なサンプルコード

まずは最もシンプルな例です。

プログラム起動から3秒後(3,000ミリ秒後)に「Hello, Timer!」を表示し、Timerをcancel()して終了します。

import java.util.Timer;
import java.util.TimerTask;

public class SimpleTimerExample {
    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("Hello, Timer!");
                timer.cancel(); // Timerを終了させないとプログラムが終わりません
            }
        };

        System.out.println("3秒後にタスクを実行します。");
        timer.schedule(task, 3000);
    }
}

繰り返し処理の例

次に、繰り返し処理の例です。プログラム開始2秒後に最初のタスクを実行し、その後は1秒ごとに現在時刻を表示し続けます。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class RepeatTimerExample {
    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("現在の時刻: " + new Date());
            }
        };

        System.out.println("2秒後から1秒ごとにタスクを繰り返し実行します。");
        // 2,000ミリ秒後に開始し、その後1,000ミリ秒ごとに繰り返す
        timer.scheduleAtFixedRate(task, 2000, 1000);
    }
}

このコードを実行すると、プログラムは終了せずに時刻を表示し続けます。手動で停止させる必要があります。

遅延実行の例

遅延実行は、最初のサンプルコードと同じscheduleメソッドで実現できます。

ここでは、少し応用して特定のDateオブジェクト(日時)を指定してみましょう。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class DelayTimerExample {
    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("指定時刻になりました!");
                timer.cancel();
            }
        };

        // 現在時刻から5秒後のDateオブジェクトを生成
        long delay = 5 * 1000;
        Date executionTime = new Date(System.currentTimeMillis() + delay);

        System.out.println(executionTime + " にタスクを実行します。");
        timer.schedule(task, executionTime);
    }
}

Timerを使う際の5つの注意点

Timerを使う際の5つの注意点を示す図解

Timerは手軽で便利ですが、実務で使うには知っておくべき重要な注意点が5つあります。

これらを知らないと、予期せぬバグやシステム障害につながる可能性があります。

なおJava 5以降はScheduledExecutorServiceが後継として推奨されており、2026年現在の新規実装では基本的にそちらを使うのがベストプラクティスです。

本記事の注意点は、レガシーコードを保守する際の罠を理解するためにも役立ちます。

マルチスレッド環境での注意

Timerは、内部で単一のスレッドしか持っていません。複数のタスクをスケジュールした場合でも、それらはすべて同じスレッドで順番に実行されます。

もし、あるタスクの処理に非常に長い時間がかかってしまうと、その間、後続のタスクはすべて待たされることになります。

これにより、期待した時刻に処理が実行されないという問題が発生します。

例外発生時の挙動

例外が発生したときの挙動は、Timerクラスの中で最も注意すべきポイントです。

TimerTaskrunメソッド内で、キャッチされない例外(RuntimeExceptionなど)が発生すると、そのTimerを管理しているスレッドが完全に停止してしまいます。

あわせて読む

スレッドが停止すると、そのTimerにスケジュールされていた他のタスクは、エラーになったタスク以降一切実行されなくなります

エラーハンドリングを怠ると、システムが静かに機能停止に陥る危険があるのです。

私の失敗談:本番で「動いているはずなのに動いていない」状態に陥った話

新人だった頃に、定期バッチの監視ジョブをTimerで実装したことがあります。

テスト環境では問題なく動いていたのに、本番リリース後に「ジョブが朝のうちは動いていたが、午後から記録が途絶えている」とアラートが上がりました。

調査するとログには何も残っておらず、JVMプロセスは生きている。なのに次のタスクが永遠に来ない。半日かけてようやく原因にたどり着きました。

正午の処理でNullPointerExceptionが出て、Timerのスレッドが死んでいたのです。

このとき強烈に学んだのが、TimerTask#run()の中身は必ずtry-catch (Throwable t)で囲み、最低でもログ出力すること。

ScheduledExecutorServiceでも基本は同じですが、Futureから例外を取得できる分こちらの方が安全です。

それ以来、新規実装でTimerを選ぶことはなくなりました。

Timerのキャンセルと終了処理

Timerは、バックグラウンドでスレッドを動かし続けます。

そのため、アプリケーションのメイン処理が終わっても、Timerが動いている限りJavaのプロセスは終了しません。

不要になったTimerは、必ずtimer.cancel()メソッドを呼び出して明示的に終了させる必要があります。

cancel()を呼ばないと、リソースリークの原因となり、サーバーアプリケーションなどでは深刻な問題につながります。

手元検証:cancel()を呼ばずにTimerを生成し続けるとどうなるか

JDK 21(Temurin 21.0.5)で、ループ内でnew Timer()scheduleを呼び続けてもcancel()を呼ばない、という雑な実装を用意しました。

VisualVM(JDK 9以降はJDKに同梱されず、visualvm.github.ioから別途入手するスタンドアロンツール)でスレッド数を観測してみました。

Timerの内部実装(単一バックグラウンドスレッドでObject.wait()に依存する構造)はJava 5以降変わっていないため、この検証結果は最新のJava 25環境でも同様に再現します。

  • 1秒ごとに新しいTimerを生成 → スレッド数が単調に増加(10秒後に約11本、1分後に約61本)
  • 同じ実装でtimer.cancel()を毎回呼ぶ → スレッド数は1〜2本で安定

実装ミスでcancel()が呼ばれないコードパスがあると、長時間稼働するサーバではスレッドリークの確実な原因になります。

finallyブロックでcancel()を呼ぶか、try-with-resourcesで囲めるScheduledExecutorServiceを選ぶのが安全です。

scheduleAtFixedRateはどんな挙動をしますか?

scheduleAtFixedRateは、タスクの開始時刻を基準にスケジュールを組みます。もしタスクの実行時間が設定した周期(period)よりも長くなった場合、タスクの実行がどんどん遅延していきます。

そして、遅延したタスクは前のタスクが終わり次第、間隔を空けずに連続で実行されることがあります。

意図しない負荷集中を引き起こす可能性があるため、タスクの処理時間には注意が必要です。

System.gc()を呼ぶとTimerの動作に影響しますか?

あまり知られていませんが、Timerの内部実装はObject.wait()メソッドに依存しています。Oracle公式のTimerクラスのドキュメントも「ガベージコレクションなどのバックグラウンド処理によって実行が遅延することがある」と明記しており、GCの一時停止(Stop-The-World)でタイマースレッドの起床が遅れ、タスクの実行タイミングがずれる可能性があります。通常は問題になりませんが、特殊な環境では注意が必要です。

Timerと他の仕組みの比較

TimerとScheduledExecutorServiceなど他の仕組みとの比較を示す図解

ここまでjava timerの注意点を見てきましたが、現在ではより高機能で安全な代替クラスが存在します。

ScheduledExecutorServiceとの違い

ScheduledExecutorServiceとは、スレッドプールを使ってタスクを定期実行できる、java.util.concurrentパッケージのインターフェースです。

Java 5(2004年)から導入され、Timerの欠点を克服した後継APIと位置づけられています。

2026年8月時点の最新LTSはJava 25(2025年9月リリース、8年間サポート)で、Java 21で正式導入された仮想スレッド(Virtual Threads)と組み合わせて使うのが一般的です。

なおJava 17は仮想スレッドを含まないため、その恩恵を受けるにはJava 21以降への移行が前提になります。

いずれにせよ、Timerを新規採用する場面はほぼありません。

あわせて読む
項目TimerScheduledExecutorService
スレッドモデルシングルスレッドスレッドプール(複数スレッド可)
例外処理タスクの例外でスレッドが停止するタスクの例外は捕捉され、他のタスクに影響しない
柔軟性TimerTaskのみ実行可能RunnableCallable(戻り値あり)も実行可能
推奨度レガシー(@Deprecatedではないが非推奨扱い)推奨

ScheduledExecutorServiceは、内部的にスレッドプールを使っているため、複数のタスクを並行して実行できます。

あるタスクが遅延しても、他のタスクに影響を与えません。また、タスク内で例外が発生しても、サービス全体(スレッドプール)が停止することはありません。

ただし、例外を起こした定期タスクそのものは以降実行されなくなる点はTimerと同じで、この点は後述の「手元検証」で詳しく確認します。

具体的に、本記事のscheduleAtFixedRateサンプルをScheduledExecutorServiceに書き換えるとこうなります。

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

        Runnable task = () -> {
            try {
                System.out.println("現在の時刻: " + new Date());
            } catch (Throwable t) {
                t.printStackTrace(); // 例外を握りつぶさず必ずログに出す
            }
        };

        // 2秒後から1秒間隔で繰り返し(Timerのscheduleと同じ挙動)
        scheduler.scheduleWithFixedDelay(task, 2, 1, TimeUnit.SECONDS);
    }
}

違いのポイントは3つです。

(1) スレッドプールサイズを引数で指定できる、(2) RunnableまたはCallableを直接渡せる(TimerTask不要)、(3) 例外でスレッドプール全体は停止しない。

Timerからの移行は機械的な置き換えに近く、難しくありません。

手元検証:ScheduledExecutorServiceの定期実行も「例外で止まる」のはTimerと同じ

「例外が発生してもサービス全体が停止しないから堅牢」という説明は正しいのですが、これだけを読むと「例外を起こしたタスク自体は次回以降も律儀に実行され続ける」と誤解しそうになったので、OpenJDK 23.0.1(java -versionで確認)で実際に検証してみました。

300ミリ秒間隔でscheduleAtFixedRateし、2回目の実行でわざとRuntimeExceptionを投げるタスクを、Timer版とScheduledExecutorService版の両方で用意し、2秒間(本来なら6〜7回実行されるはず)待ってから実行回数を数えました。

実装2秒後の実行回数例外後の挙動
Timer#scheduleAtFixedRate2回で停止Timer-0スレッドが例外で終了し、以降すべてのタスクが実行されなくなる
ScheduledExecutorService#scheduleAtFixedRate2回で停止スレッドプール自体は生きているが、例外を起こしたScheduledFutureの以降の実行だけが暗黙に中止される(future.isDone()trueになり、future.get()ExecutionExceptionとして例外を取得できる)

つまり、「サービス全体が停止しない」のは事実ですが、その定期タスク自体は例外発生後に黙って止まるという点はTimerと変わりません。

違いは、Timerだと同じスレッドに乗っていた他のタスクまで巻き添えで止まる点です。

一方ScheduledExecutorServiceでは影響範囲がそのタスクだけに限定され、Future経由で異常終了を検知できます。

定期実行を止めたくない場合は、タスクの中身をtry-catchで必ず囲む、という結論は変わりません。

scheduleWithFixedDelayscheduleAtFixedRateの違い

上のコード例ではscheduleWithFixedDelayを使いましたが、ScheduledExecutorServiceにはscheduleAtFixedRateも存在します。

両者の違いは、TimerのscheduleAtFixedRateschedule(task, delay, period)の関係とほぼ同じです。

  • scheduleAtFixedRate(command, initialDelay, period, unit): 前回タスクの開始時刻を基準に次回を計算する(TimerのscheduleAtFixedRate相当)
  • scheduleWithFixedDelay(command, initialDelay, delay, unit): 前回タスクの終了時刻を基準に次回を計算する(Timerのschedule(task, delay, period)相当)

正確な周期を厳密に維持したい場合はscheduleAtFixedRate、処理の重なりを避け負荷を平準化したい場合はscheduleWithFixedDelayを選ぶのが基本方針です。

ScheduledExecutorServiceのシャットダウン処理を忘れずに

ScheduledExecutorServiceはTimerと違い、明示的にshutdown()(またはshutdownNow())を呼ばない限りスレッドプールが動き続けます。

Timerのcancel()に相当する終了処理を忘れると、アプリケーション終了時にJVMが正常終了しない、あるいはスレッドリークにつながる点は共通の落とし穴です。

  • shutdown(): 新規タスクの受付を停止し、実行中・待機中のタスクは完了させてから終了する
  • shutdownNow(): 実行中のタスクを中断し、待機中のタスクも破棄して即座に終了する

アプリケーションのライフサイクルに合わせて、@PreDestroyやシャットダウンフックでshutdown()を呼び出す実装にしておくと安全です。

実務でよく使われるケース

新規でスケジュール処理を実装する場合、ScheduledExecutorServiceを利用するのが一般的です。

Timerは、その多くの欠点から、現在では積極的に利用される場面はほとんどありません。

ただし、古いシステムの保守や既存ライブラリのコードリーディングでjava timerに出会う場面はまだあります。

Timerの仕組みや注意点を理解しておくことは、Javaエンジニアにとって今でも実用的な知識です。

私の判断基準としては、「レガシーのTimer実装を見つけたら即座に置き換えるべきか」は一律には決めていません。

本番で稼働中かつ障害実績がないなら、無理に触らないのが基本方針です。

前述の「手元検証」で示したとおり、例外で1本のスレッドが死んでも他プロセスに波及しない設計であれば、リスクとリファクタリングコストが見合わないことが多いためです。

逆に、複数のタスクを1つのTimerに相乗りさせている実装(=1つの例外が他のタスクまで巻き添えにする構造)を見つけたときは話が別です。

優先度を上げてScheduledExecutorServiceへの置き換えを提案しています。

Timerからの移行チェックリスト

既存のTimer実装をScheduledExecutorServiceに置き換える際は、以下の4点を順に確認すると移行漏れを防げます。

  • TimerTaskをRunnable/Callableに置き換える: run()メソッドの中身をそのままRunnableのラムダに移す
  • scheduleとscheduleAtFixedRateの対応を確認する: 開始時刻基準ならscheduleAtFixedRate、終了時刻基準ならscheduleWithFixedDelayを選ぶ
  • cancel()をshutdown()に置き換える: 終了処理を書き忘れるとTimer時代と同じくスレッドリークになる
  • 例外処理をtry-catchからFutureベースに見直す: submit()の戻り値Futureからget()で例外を取得できるようにしておくと、握りつぶしを防げる

Spring @Scheduledやcronとの関係

Spring Bootを使っているプロジェクトでは、java timerを直接書く場面はさらに少なくなります。

Springの@Scheduledアノテーションを使うと、メソッドにアノテーションを付けるだけで定期実行が実装できるためです。

  • @Scheduled(fixedRate = 1000): scheduleAtFixedRate相当(開始時刻基準)
  • @Scheduled(fixedDelay = 1000): schedule(task, delay, period)相当(終了時刻基準)
  • @Scheduled(cron = "0 0 0 * * *"): cron式で柔軟なスケジュール

OSレベルのcronやKubernetesのCronJobと比べると、Spring側で実装する利点はアプリ内のDIコンテナや例外通知の仕組みをそのまま使えることです。

常時起動のWebアプリならSpringで、バッチ単発実行ならOS cronで、と役割を分けるのが定石です。

あわせて読む

私の失敗談:@Scheduledで起動したバッチが本番でOOMを起こした話

Spring Batchを@Scheduledで定期起動する構成で、chunk sizeを1000に設定して本番投入したところ、想定より対象データが多い日にメモリ不足(OutOfMemoryError)でジョブが落ちたことがあります。

原因調査には2時間ほどかかりましたが、修正自体はchunk sizeを100に下げるだけの5分で完了しました。

この一件で学んだのは、「定期実行の仕組み(Timer@Scheduledか)をどれだけ堅牢にしても、実行される処理自体のリソース設計が甘ければ意味がない」ということです。

TimerScheduledExecutorServiceのスレッド管理・例外処理を丁寧に作り込んでも、中身のバッチ処理がメモリを食い潰せば同じことが起きます。

chunk sizeやバッチサイズは、本番データ量を想定してまず小さめに始めるのが安全です。

Java Timerに関するよくある質問

Q. Timerは非推奨ですか?

公式に「非推奨」(@Deprecated)にはされていませんが、Java 5以降はScheduledExecutorServiceが事実上の後継として位置づけられています。新規開発でわざわざTimerを選ぶ理由はほぼありません。

Q. Timerのスレッド数は変更できますか?

Timerはシングルスレッド固定で、スレッド数を増やす設定はありません。並列実行が必要ならExecutors.newScheduledThreadPool(n)でスレッド数を指定できるScheduledExecutorServiceを使ってください。

Q. cancel()を呼ばないとどうなりますか?

内部のバックグラウンドスレッドが動き続け、Javaプロセスが終了しません。mainメソッドが返ってもJVMが終了しないように見えるのはこれが原因です。Webアプリやサーバ上でTimerのリークが続くと、リソース枯渇の温床になります。

Q. ミリ秒以下の精度は出ますか?

JVMとOSのスケジューラに依存しますが、ミリ秒精度を保証するものではありません。高精度が必要な場面ではScheduledExecutorServiceSystem.nanoTime()を使うか、リアルタイムJVMの導入を検討してください。

Q. TimerとScheduledExecutorServiceは例外発生時に同じ挙動になりますか?

はい、「例外を起こした定期タスクが以降実行されなくなる」という結果自体は同じです。違うのは影響範囲で、Timerは同じスレッドに乗っていた他のタスクまで巻き添えで止まりますが、ScheduledExecutorServiceはそのタスクだけに影響が限定され、Future経由で例外を検知できます。詳しくは本記事の「手元検証」で実測結果を紹介しています。

Q. ScheduledExecutorServiceのshutdown()を呼び忘れるとどうなりますか?

Timerのcancel()忘れと同様、スレッドプールが動き続けてJavaプロセスが終了しません。長時間稼働するアプリケーションではスレッドリークの原因になるため、@PreDestroyやシャットダウンフックでshutdown()を呼ぶ実装にしておく必要があります。

Q. Spring BootならTimerより@Scheduledを使うべきですか?

はい、Spring Bootプロジェクトでは@Scheduledを使うのが基本です。DIコンテナや例外通知の仕組みをそのまま使え、fixedRate/fixedDelay/cronで用途に応じた指定もできます。Timerを直接書く必要があるのは、Springを使わない小規模なJavaプログラムやレガシーコードの保守に限られます。

まとめ

今回はJavaのTimerクラスについて、基本的な使い方から実務で役立つ注意点まで詳しく解説しました。

Timerを理解するメリット

Timerは古い技術ですが、その仕組みを学ぶことで、タスクスケジューリングの基本的な考え方や、シングルスレッドモデルの課題を深く理解できます。

なぜScheduledExecutorServiceのような後継技術が必要になったのか、その背景を知る上でもTimerの知識は役立ちます。

次に学ぶべき関連クラス

Timerの学習を終えたら、次はぜひ以下のクラスについて学んでみてください。

  • ScheduledExecutorService: Timerのモダンな代替技術です。より堅牢で柔軟なスケジュール管理ができます。
  • ExecutorService: Javaの並列処理の基本となるインターフェースで、スレッドプールを簡単に扱うための仕組みです。
  • CompletableFuture: Java 8から導入された、非同期処理をより高度に、そして簡潔に記述するためのクラスです。
  • Virtual Threads(Java 21で正式導入、2026年時点の最新LTSであるJava 25でも安定運用中): 軽量スレッドで、大量の並行スケジュールを低コストで処理できます。ScheduledExecutorServiceとの組み合わせで真価を発揮します。
  • Spring @Scheduled: Spring Bootアプリケーションでの定期実行のデファクトスタンダードです。

これらの技術を習得することで、Javaでの並行処理や非同期処理への理解が一層深まるでしょう。

あわせて読む
  • この記事を書いた人
  • 最新記事

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

-Java入門