定期的にタスクを実行させる

【前提条件】

[環境]
[参考資料]

Concurrency Utilities for JavaEE 1.0
The Java Community Process(SM) Program - communityprocess - final

[その他]

コンテキストパスは「Sample」、ApplicationPathは「/service」。

並行処理リソースはGlassFishのデフォルトで
組み込まれているものを使用しています。

【概要】

今回は非同期処理を定期的に実行させる方法についてです。

サンプルではJAX-RSから実行していますが、
基本的にJavaSEのCunncurrency UtilitiesのAPIを使っています。

【タスク】

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

public class ScheduledTask implements Runnable {
 
    private final TimeUnit unit;

    private final String type;

    public ScheduledTask(final TimeUnit unit, final String type) {

        this.unit = unit;
        this.type = type;
    }

    @Override
    public void run() {

        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println(type + " : " + unit + " : " + new Date());

    }
}

実行するタスクはコンストラクタで受け取った値と日付を出力させるものを作成します。

Stringクラスのオブジェクトは実行されたタスクを識別するために使用しています。
TimeUnitクラスのオブジェクトについては後で説明します。

【リソースクラス】

import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.enterprise.concurrent.ManagedScheduledExecutorService;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import jp.glory.sample02.task.ScheduledTask;

@Path("/sample02")
public class ScheduleSampleResource {

    @Resource
    private ManagedScheduledExecutorService service = null;

    @POST
    @Path("/callTask")
    public Response callTask() {


        System.out.println("################### Start call task ###################");
        service.schedule(new ScheduledTask(TimeUnit.SECONDS, "Time Unit Only1"), 5, TimeUnit.SECONDS);
        service.schedule(new ScheduledTask(TimeUnit.MINUTES, "Time Unit Only2"), 1, TimeUnit.MINUTES);

        service.scheduleAtFixedRate(new ScheduledTask(TimeUnit.SECONDS, "At Fixed Rate"), 5, 3, TimeUnit.SECONDS);
        service.scheduleWithFixedDelay(new ScheduledTask(TimeUnit.SECONDS, "With Fixed Delay"), 5, 3, TimeUnit.SECONDS);
        System.out.println("################### End call task ###################");

        return Response.ok("task is started.").build();
    }
}

定期的に実行させるタスクを使うためには
javax.enterprise.concurrent.ManagedScheduledExecutorServiceクラスのオブジェクトを
Resourceアノテーションでインジェクションします。

サンプルでは指定した経過時間後に1度だけ実行させるタスクと
定期的に実行するタスクを作成します。

[一度だけ実行する]

1度だけタスクを実行させる場合はscheduleメソッドを使います。
scheduleのパラメータはRunnable command, long delay, TimeUnit unitの3つです。

commandには実行したいタスクを渡します。

unitにはjava.util.concurrent.TimeUnitクラスのオブジェクトを渡します。
TimeUnitは列挙型で、時間の単位を指定するためのクラスです。

delayとunitを組み合わせて、実行させる間隔を指定します。
サンプルでは5秒後(5, TimeUnit.SECONDS)と1分後(1, TimeUnit.MINUTES)を指定しています。

[定期的に実行する]

定期的にタスクを実行させるにはscheduleAtFixedRateメソッド、
scheduleWithFixedDelayメソッドを使用します。

両メソッドともにパラメータは
Runnable、long、long、TimeUnitの順番に指定します。

RunnableとTimeUnitはscheduleメソッドと同様です。
2番目に指定するlongのパラメータは初回実行の間隔、
3番目に指定するlongは2回目以降の間隔になります。

scheduleAtFixedRateメソッドは初回実行の後、
3番目に指定した値ごとに実行されます。
サンプルでは3秒ごとに実行されます。

scheduleWithFixedDelayメソッドは初回実行の後、
タスクが終了してから3番目に指定した値の分だけ待った後、実行されます。
サンプルでは処理が終了した3秒後にタスクが実行されます。

【実行結果】

作成したJAX-RSのリソースクラスを実行して見ます。

実行後、1分ほど待つと↓のような結果がコンソールに出力されます。

情報:   ################### Start call task ###################
情報:   ################### End call task ###################
情報:   Time Unit Only1 : SECONDS : Sun Oct 27 17:18:16 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:18:17 JST 2013
情報:   With Fixed Delay : SECONDS : Sun Oct 27 17:18:18 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:18:19 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:18:22 JST 2013
情報:   With Fixed Delay : SECONDS : Sun Oct 27 17:18:23 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:18:25 JST 2013
情報:   With Fixed Delay : SECONDS : Sun Oct 27 17:18:27 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:18:28 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:18:31 JST 2013
情報:   With Fixed Delay : SECONDS : Sun Oct 27 17:18:32 JST 2013

(中略)

情報:   With Fixed Delay : SECONDS : Sun Oct 27 17:19:03 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:19:04 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:19:07 JST 2013
情報:   With Fixed Delay : SECONDS : Sun Oct 27 17:19:08 JST 2013
情報:   At Fixed Rate : SECONDS : Sun Oct 27 17:19:10 JST 2013
情報:   Time Unit Only2 : MINUTES : Sun Oct 27 17:19:11 JST 2013

最初にJAX-RSのリソースクラスの出力が表示されます。

5秒後に[Time Unit Only1]のタスクが実行されます。
1分後に[Time Unit Only2]のタスクが実行されます。

[At Fixed Rate]は秒の桁が
17秒⇒19秒⇒22秒⇒25秒⇒・・・⇒04秒⇒07秒と
初回実行後、3秒ごとに実行されていることがわかります。
(2回目の実行だけ、2秒後ですが・・・)

[With Fixed Delay]は秒の桁が
18秒⇒23秒⇒27秒⇒31秒⇒・・・⇒03秒⇒08秒と
初回実行後、4,5秒ごとに実行されていることがわかります。

【まとめ】

を実行する方法は
JDK5.0からCunncurrency Utilitiesで提供されていました。

JavaEEからはインジェクションすることで、
指定間隔ごとに処理を実行できるようになっています。

Webアプリケーションで定期実行処理をさせることは少ないですが、
手段として覚えておいてもよさそうです。