java.util.Timer / java.util.TimerTask

目的
一定時間置きに処理をする/指定時間に処理をする

関連クラス

今回のソース
//////////////////// test1.java ////////////////////
// 一定時間置きに処理を行うサンプル

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

/**
 * java.util.Timer / java.util.TimerTaskのサンプル
 * @author taka
 */
public class test1
{
	public static void main(String args[]) throws Exception
	{
		Timer timer = new Timer();
		timer.schedule(new TestTimer(), 0, 1000);
	}

	/**
	 * 指定間隔置きに呼ばれる処理
	 * @author taka
	 */
	static class TestTimer extends TimerTask
	{
		public void run()
		{
			System.out.println("Hello, World!");
		}
	}
}

////////////////////////////////////////////////////////////

//////////////////// test2.java ////////////////////

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

/**
 * java.util.Timer / java.util.TimerTaskのサンプル
 * @author taka
 */
public class test2
{
	public static void main(String args[]) throws Exception
	{
		new test2();
	}

	public test2() throws Exception
	{
		Timer timer = new Timer();
		TestTimer task = new TestTimer();
		
		// 初回スケジュール時間の算出
		Date scheduledDate = addMinuteClearSecond(new Date());

		while(true)
		{
			// スケジュール中はスキップ
			if(!task.isRunning())
			{
				// 次回のスケジュールをセット
				task = new TestTimer();
				timer.schedule(task, scheduledDate);

				// スケジュール中であることをセット
				task.setRunning(true);

				// 次回スケジュール時間をセット
				scheduledDate = addMinuteClearSecond(scheduledDate);
			}

			Thread.sleep(500);
		}
	}

	/**
	 * 1分足して秒を0にセットする
* ex) 14:30:24 → 14:31:00 * @param d Date * @return 次の分の0秒 */ private static Date addMinuteClearSecond(Date d) { Calendar cal = Calendar.getInstance(); cal.setTime(d); // +1[min] cal.add(Calendar.MINUTE, 1); // 0[sec] cal.set(Calendar.SECOND, 0); return cal.getTime(); } /** * スケジュールされた時間に呼ばれる処理 * @author taka */ class TestTimer extends TimerTask { // スケジュールされているかどうかを表すフラグ boolean isReady = false; /** * スケジュールされた時間に呼ばれる処理(本体) */ public void run() { System.out.println(new Date() + ": Hello, World!"); setRunning(false); } /** * スケジュールされているかどうかをセットする * @author taka */ public void setRunning(boolean isRunning) { this.isReady = isRunning; } /** * スケジュールされているかどうかを返す * @author taka */ public boolean isRunning() { return isReady; } } } ////////////////////////////////////////////////////////////
Source is here. (ZIP Format,1623Bytes,Shift-JIS)

コンパイル&実行
javac test1.java / test2.java
java test1 / java test2;

説明
(概略)

とあるプロジェクトで、Javaで常駐処理なるものを書いたのがコトの始まりだったりします。

常駐処理は、例えばHTTPサーバとか、MAILサーバのような、リクエストに応じて処理を行う、
いわゆるイベントドリブンとか呼ばれるものも分類されますし、
今回僕が作った、数秒のスリープをはさんで、同一の処理を延々と行うもので、
時間間隔起動とでもいうような処理も含まれると思います。

その時使用したのは、Threadを用いた「コテコテ」な処理で、
メインループを書いて、Thread.sleepをはさんでスリープさせ、
業務処理をそこに書いていくというものでしたが、
上記test1.javaの例をみれば分かるとおり、
Timer / TimerTaskを利用すれば、呼ぶ側も呼ばれる側も、すっきりした記述で済むことが分かります。
(もっとも、このクラスの存在に気づいたのは、終わった後でしたが。。。)

ちなみに、思いつきなんですが、test2.javaの方は、
UNIXのcronの真似事のようなことをしています。
毎分0秒の時に、タスクを実行するようになっています。
(結構まわりくどいことしてますが、もっとすっきり書けるかも知れません。)

常駐している処理を外部から止めるには?
とあるプロジェクトでは、外部ファイルに起動/停止の状態を書いておき、
それを随時監視するという方法で行っていましたが、
大量のI/Oが発生するし、あまり効率の良い方法とは言えませんね。

そこでまたまた思いつきなんですが、
タスクとは別のスレッドでSocketサーバをあるポートで立てておき、
そこに接続があった場合、タスクを止めるという方法であれば、
比較的リソースの消費は少ないと思います。
まあ、ポート番号の管理という別の問題が発生しますが。

というわけで、Javaで以下のような処理を記述する場合は、
Timer / TimerTaskの使用を検討してみてください。
◇時間起動(cronのようなもの)
◇時間間隔起動(監視処理のようなもの; 同じ処理を等間隔で実行)