連載
» 2004年08月17日 10時00分 公開

JavaTips 〜Javaプログラミング編:Javaのタスクを定期的に実行する

[BULL,@IT]

 Javaのメソッドとして実装されたタスクがあり、そのタスクを定期的(一定間隔)に実行したい場合は、java.util.TimerTaskクラスとjava.util.Timerクラスを使用すると便利です。これらのクラスの利用方法は、以下のようになります。

  • java.util.TimerTaskを継承したクラス(例えばTask1)を作成し、タスクとして実行したい処理をrunメソッドとして実装する
  • java.util.Timerクラスのオブジェクトを作成し、Task1クラスのオブジェクトと、実行開始時刻や実行間隔をscheduleメソッドの引数として渡す

 これらの処理により、Task1クラスのrunメソッドが定期的に実行されます。その際、必ず前回のタスクの処理が終了してから、次のタスクが実行されます。タスク処理を中断する場合は、java.util.Timer#cancelメソッドを呼び出します。

 java.util.Timerクラスのメソッド一覧は、以下のとおりです。

java.util.Timerクラスのメソッド一覧
メソッド 意味
void cancel() タスク処理を中断する
void schedule(TimerTask task, Date time) taskオブジェクトのrunメソッドを時刻timeに1度だけ実行する
void schedule(TimerTask task, long delay) taskオブジェクトのrunメソッドを現在時刻からdelay(ミリ秒)後に1度だけ実行する
void schedule(TimerTask task, Date firstTime, long period) taskオブジェクトのrunメソッドを時刻timeを開始時点としてperiod(ミリ秒)間隔で実行する
void schedule(TimerTask task, long delay, long period) taskオブジェクトのrunメソッドを現在時刻からdelay(ミリ秒)後を開始時点としてperiod(ミリ秒)間隔で実行する
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) taskオブジェクトのrunメソッドを時刻timeを開始時点としてperiod(ミリ秒)間隔で実行する
void scheduleAtFixedRate(TimerTask task, long delay, long period) taskオブジェクトのrunメソッドを現在時刻からdelay(ミリ秒)後を開始時点としてperiod(ミリ秒)間隔で実行する

scheduleメソッドとscheduleAtFixedRateメソッドの違い

 java.util.Timerクラスのメソッドのうち、一定間隔で引数taskのrunメソッドを実行するものとして、scheduleメソッドとscheduleAtFixedRateメソッドの2つがあります。これらの違いは、以下のとおりです。

  • scheduleメソッドは、直前のタスクの実行開始時刻を基準として、それからperiod(ミリ秒)後に次のタスクを実行しようと試みる
  • scheduleAtFixedRateメソッドは、最初のタスクの実行開始時刻を基準として、n回目に実行されるタスクをperiod×n(ミリ秒)後に実行しようと試みる

 どちらのメソッドでも、n回目のタスクの実行予定時刻が過ぎた時点で、n-1回目までのタスクの実行が終了していない場合は、n-1回目までのタスクの実行終了を待ち、終了直後にn回目のタスクの実行を開始します。つまり、毎回実行されるすべてのタスクの実行時間が、period(ミリ秒)以内であれば、双方のメソッドの動作は同じになります、

 しかし、あるタスクの実行時間がperiod(ミリ秒)を超えた場合は動作が異なってきます。1回目のタスクの実行時間がperiod(ミリ秒)を超えた場合の、それぞれのメソッドでの動作の違いを以下に示します。

scheduleメソッドとscheduleAtFixedRateメソッドの違い scheduleメソッドとscheduleAtFixedRateメソッドの違い

サンプルコード

 java.util.TimerTaskクラスとjava.util.Timerクラスを利用して定期的にタスクを実行するサンプルコードは以下のとおりです。

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

class Task1 extends TimerTask {
    private volatile boolean isFirst = true;
    private volatile int taskNum = 1;
    public void run() {
        System.out.println(taskNum + " " + System.currentTimeMillis());
        taskNum++;
        if (isFirst) {
            isFirst = false;
            try {
                Thread.sleep(2000); // 1回目のタスクの実行には2000ミリ秒かかる
            } catch (InterruptedException ignore) {
            }
        }
    }
}

public class TaskSample {
    public static void main(String[] args) {
        System.out.println("---schedule()---");
        Timer timer1 = new Timer();
        timer1.schedule(new Task1(), 0, 1000); // タスクの実行間隔は1000ミリ秒
        try {
            Thread.sleep(5000);
        } catch (InterruptedException ignore) {
        }
        timer1.cancel();

        System.out.println("---scheduleAtFixedRate()---");
        Timer timer2 = new Timer();
        timer2.scheduleAtFixedRate(new Task1(), 0, 1000); // タスクの実行間隔は1000ミリ秒
        try {
            Thread.sleep(5000);
        } catch (InterruptedException ignore) {
        }
        timer2.cancel();
    }
}


 このサンプルコードは、java.util.TimerクラスのscheduleメソッドとscheduleAtFixedRateメソッドの違いも示しています。最初のタスクの実行時間(2000ミリ秒)が実行間隔(1000ミリ秒)を超えるようにしてあるため、scheduleメソッドを使用した場合と、scheduleAtFixedRateメソッドを使用した場合で、タスクの実行タイミングが異なります。

 このサンプルコードの実行結果は次のとおりです。

実行結果
---schedule()---
1 1089554378000
2 1089554380000
3 1089554381000
4 1089554382000
---scheduleAtFixedRate()---
1 1089554383000
2 1089554385000
3 1089554385000
4 1089554386000
5 1089554387000


 この実行結果を見ると、最初のタスクの実行開始時刻を基準とすると、scheduleメソッドを使用した場合、2番目のタスクは2000ミリ秒後に、3番目のタスクは3000ミリ秒後に実行されています。それに対して、scheduleAtFixedRateメソッドを使用した場合は、2番目のタスクの実行直後に3番目のタスクが実行され、(2番目のタスクの実行時間が1ミリ秒未満ととても短いため)2番目と3番目のタスクはともに2000ミリ秒後に実行されているのが分かります。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。