【Java】指定時間に実行、定期実行を簡単に実装する

2018年2月10日

ゲーム制作をしていてカウントダウン機能をつけたくなったのでTimerクラス、TimerTaskクラスの使用方法を確認しておきます。

定期実行機能は普通の業務でも利用する機会はありそうですね。私はどちらかというとExecutorsから利用するほうを良く使います。

とりあえず今回は普通のjava.util.Timer系のクラスを取り上げたいと思います。

TimerTaskの実装とTimer

まずはTimerTaskクラスのrunメソッドを実装します。

指定時間実行などで処理したい内容を記述すればいいわけです。

こんなかんじ

import java.util.TimerTask;

public class Test {
	public static void main(String[] args) {
		TimerTask task = new TimerTask() {
			
			@Override
			public void run() {
				//ここに定期実行させたい処理を記述
				System.out.println("てすと");
			}
		};
	}
}

次にTimerクラスのメソッドを呼び出します。
schedule(TimerTask task, Date time)
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

などのメソッドを呼び出しましょう。

途中でタイマーを解除する場合はTimerのcancelメソッドかTimerTaskのcancelメソッドを呼び出しましょう。

Timerを完全に終了する場合はTimerのcancelメソッドを呼び出し、渡したTimerTaskの処理だけ終了する場合はTimerTaskのcancelメソッドを呼び出します。

また、Timerのインスタンスを作成する時にtrueを設定しておくことで、デーモンスレッド化できます。

※デーモンスレッド化しておくと、通常のスレッドが終了したときにアプリも終了するようになります。デーモン化していない場合はTimerのcancelが呼び出されない限りアプリが終了しなくなります。もちろん特定の方法で終了させることはできますが・・・

指定時間に処理を実行する

Timerのschedule(処理オブジェクト, 日付時間)メソッドを呼び出すことで、特定の時間に処理を実行できます。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
	public static void main(String[] args) throws ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		Timer timer = new Timer(false);
		TimerTask task = new TimerTask() {

			@Override
			public void run() {
				System.out.println("てすと");
				timer.cancel();
			}
		};
		timer.schedule(task, sdf.parse("2018/02/06 09:11:11"));
	}
}
実行結果

てすと

上記のソースの2018/02/06 09:11:11を実行させたい時間に変更して試してみてください。

現時刻から指定時間後に処理を実行する

Timerのschedule(処理オブジェクト, ミリ秒)メソッドを呼び出すことで、特定の数秒後等に処理を実行したりできます。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
	public static void main(String[] args) throws ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		Timer timer = new Timer(false);
		TimerTask task = new TimerTask() {

			@Override
			public void run() {
				System.out.println("てすと");
				timer.cancel();
			}
		};
		timer.schedule(task, 1000);
	}
}
実行結果

てすと

アプリを実行してから1秒後に処理が実行されるはずです。

定期実行(固定遅延実行)

定期実行の固定遅延実行はschedule(task, 最初の開始時間 or 最初の開始を何秒後にするか, 定期実行期間)
で設定可能です。

固定遅延実行は出来る限り実行間隔を維持しながら処理を実行する特徴があります。

例えば、500ミリ秒ごとに実行設定した場合で1回目の処理に200ミリ秒かかってしまった場合は、次に実行されるのは1回目の処理が終わった後の300ミリ秒後になります。

import java.text.ParseException;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
	public static void main(String[] args) throws ParseException {
		Timer timer = new Timer(false);
		TimerTask task = new TimerTask() {

			int cnt=0;

			@Override
			public void run() {
				System.out.println("てすと");
				cnt++;
				//5回実行で停止
				if ( cnt >= 5 ) timer.cancel();
			}
		};
		timer.schedule(task, 0, 1000);
	}
}

実行結果

てすと
てすと
てすと
てすと
てすと

1秒ごとに処理が実行されるはずです。

定期実行(固定頻度実行)

定期実行の固定頻度実行はscheduleAtFixedRate(task, 最初の開始時間 or 最初の開始を何秒後にするか, 定期実行期間)
で設定可能です。

固定頻度実行は絶対時間での実行回数を維持しようとする特徴があります。

例えば、500ミリ秒ごとに実行設定した場合で1回目の処理に1000ミリ秒、2回目の処理も1000ミリ秒かかってしまった場合実際にかかった時間は2000ミリ秒ですが2000ミリ秒の地点で実行されるべき回数は4回です。まだ二回しか呼び出されていないので、4回呼び出すまで待ち時間なしで呼び出します。これは実行回数を4回に合わせようとするからです。

import java.text.ParseException;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
	public static void main(String[] args) throws ParseException {
		Timer timer = new Timer(false);
		TimerTask task = new TimerTask() {
			long beforeTime = System.currentTimeMillis();
			int cnt=0;

			@Override
			public void run() {
				long t = System.currentTimeMillis();
				System.out.println("前回実行終了時間から今回実行開始されるまでにかかった時間="+(t - beforeTime));
				cnt++;
				//5回実行で停止
				if ( cnt <= 2 ) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} else if ( cnt >= 8 ) {
					timer.cancel();
				}
				beforeTime = System.currentTimeMillis();
			}
		};
		timer.scheduleAtFixedRate(task, 0, 500);
	}
}
実行結果

前回実行終了時間から今回実行開始されるまでにかかった時間=0
前回実行終了時間から今回実行開始されるまでにかかった時間=0
前回実行終了時間から今回実行開始されるまでにかかった時間=0
前回実行終了時間から今回実行開始されるまでにかかった時間=0
前回実行終了時間から今回実行開始されるまでにかかった時間=0
前回実行終了時間から今回実行開始されるまでにかかった時間=499
前回実行終了時間から今回実行開始されるまでにかかった時間=500
前回実行終了時間から今回実行開始されるまでにかかった時間=500

さいしょの二回が予定より時間のかかる処理だったため次回実行までの期間が短くなっています。

遅れを取り戻したら通常通り約500ミリ秒ごとの実行となっています。

Java

Posted by nompor