これまでJavaでバッチを作成して、Javaのバッチを起動するシェルスクリプトをcronから起動するなんて事をやっていたのですが、JavaVMを常時起動させてもっと短い間隔で処理をしたいと思っていました。
追記:
java.util.Timerクラスで全く同じことができるようです。知らなかった。。
やろうとしたこと
・10秒に1回処理をしたい
・多重に起動したくない(簡単なら処理終了から10秒経過したタイミングで再度実行したい)
昔に組んだシステムではcronで5分毎にJavaのバッチを起動し、さらにJavaのバッチ側で特定のポートのListenを試行して、多重起動を防止していました。
この実装では処理終了から5分ではなく、5分に1回動こうとしているため、バッチが6分かかった場合などに4分間隙間が空いていました。
それを防ごうと機能を追加していった結果アプリケーションの動作フロー全体がスパゲティになっていた。。閑話休題。
ScheduledExecutorService
探していたら目的にぴったりの標準APIがありました。
1 2 3 4 5 6 7 8 |
ScheduledExecutorService serviceExecutor = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture future = serviceExecutor.scheduleWithFixedDelay(() -> { //やりたい処理 }, 1, 10, TimeUnit.SECONDS); // キャンセルするとき future.cancel(false); // true .. 処理が動いていたらinvokeする false .. invokeしない serviceExecutor.shutdown(); // これを書かないとプロセスは死なない |
こんな感じ。
このAPIが追加されたのは1.5のようですが、上のサンプルコードではラムダ式を使っているのでJava8の必要があります。
サンプルコード中で使用しているscheduleWithFixedDelayメソッドを使用すると、実行後からの時間を指定できます。
scheduleAtFixedRateメソッドにすると実行時間に関係なく可能な限り指定時間ごとに呼び出されるようです。こちらは多重起動もありえるみたい。
今回のコードではnewSingleThreadScheduledExecutor()でScheduledExecutorServiceを作成しているので、多重起動は起こりません。
単発で時間指定して処理を走らせることもできる。詳しくはJavadoc参照。
mainメソッドが終わってもScheduledExecutorServiceが生きているとVMのプロセスは死なないので注意してください。
確かめてみるとこんな感じ。
・ScheduledExecutorServiceがnewされて、何もscheduleしていないとプロセスは止まる。
・何かscheduleした場合はfutureのcancelだけではプロセスは生き残る。
・shutdownしたらプロセスは止まる。
おまけ:cronもどきのライブラリ
実はcronで秒まで指定できないのを失念していたので色々探してしまった。
ただ、曜日や毎時10分と15分、などといった複雑な条件を指定したければライブラリを使うしかなさそうなので機会があれば使うかも。
・cron4j
cronとして使うにはシンプルだったが、秒単位での動作はできなかった(当たり前)。
・quartz
簡単には使えそうになかったので確かめなかった。