web-dev-qa-db-ja.com

Spring Quartzのジョブ実行が重複しないようにします

20秒ごとにSpring Qquartzから実行するJavaプログラムがあります。実行するのに数秒かかることもありますが、データが大きくなると、20秒以上実行されるはずです。

1つのインスタンスがまだ実行されている間に、Quartzがジョブを起動/トリガーしないようにするにはどうすればよいですか?データベース上で同じ操作を実行する2つのジョブを実行することはあまり良くありません。何らかの同期を行う方法はありますか?

45
ant

あなたがする必要があるのが20秒ごとに発射するだけなら、クォーツは深刻な過剰です。 Java.util.concurrent.ScheduledExecutorServiceは、その仕事に完全に十分でなければなりません。

ScheduledExecutorServiceは、スケジューリングのための2つのセマンティクスも提供します。 "fixed rate" は、オーバーラップに関係なく20秒ごとにジョブを実行しようとしますが、 "fixed delay" は、最初のジョブの終了と次の始まり。重複を避けたい場合は、固定遅延が最も安全です。

31
skaffman

クオーツ1

Jobの代わりにStatefulJobを実装するようにクラスを変更すると、Quartzがこれを処理します。 StatefulJob javadoc から:

ステートフルジョブは同時に実行できません。つまり、execute(xx)メソッドの完了が遅れる前に発生する新しいトリガーが発生します。

StatefulJobはJobを拡張し、新しいメソッドを追加しません。したがって、目的の動作を得るために必要なことは、これを変更することだけです。

public class YourJob implements org.quartz.Job {
    void execute(JobExecutionContext context) {/*implementation omitted*/}
}

これに:

public class YourJob implements org.quartz.StatefulJob {
    void execute(JobExecutionContext context) {/*implementation omitted*/}
}

クオーツ2

Quartzのバージョン2.0では、StatefulJobは非推奨です。代わりに注釈を使用することをお勧めします。

@DisallowConcurrentExecution
public class YourJob implements org.quartz.Job {
    void execute(JobExecutionContext context) {/*implementation omitted*/}
}
133
Dónal

誰かがこの質問に言及した場合に備えて、StatefulJobは廃止されました。代わりにアノテーションを使用することをお勧めします...

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class TestJob implements Job {

これにより、これらの注釈の意味が説明されます...

注釈は、その名前が示すとおりの動作を引き起こします-ジョブの複数のインスタンスを同時に実行することは許可されません(ジョブのexecute()メソッドに実行に34秒かかるコードがありますが、 30秒ごとに繰り返されるトリガー)、および各実行後にJobDataMapの内容がスケジューラのJobStoreに再保持されます。この例では、@ PersistJobDataAfterExecutionアノテーションのみが本当に関連していますが、保存されたデータの競合状態を防ぐために@DisallowConcurrentExecutionアノテーションを使用することが常に賢明です。

19
gshauger

スプリングクオーツを使用する場合、このように設定する必要があると思います

    <bean id="batchConsumerJob"class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="myScheduler" />
        <property name="targetMethod" value="execute" />
        <property name="concurrent" value="false" />
    </bean>
4
Paul

2番目のタスクは最初のタスクが終了するまでブロックされ、バックログが発生するため、同期が必要かどうかはわかりません。ジョブをキューに入れることもできますが、説明からはキューが無期限に大きくなる可能性があります。

ReadWriteLock sを調査し、実行中にタスクにロックを設定します。将来のタスクはこのロックを検査し、古いタスクがまだ実行中の場合はすぐに終了できます。経験から、これが最も信頼できるアプローチであることがわかりました。

おそらく警告も生成するので、問題が発生していることがわかり、それに応じて時間間隔を増やしますか?

2
Brian Agnew

セマフォを使用できます。セマフォが取得されたら、2番目のジョブを中止し、次の起動時間まで待機します。

0
Salandur

それらをキューに入れる

時間が20秒を超える場合でも、現在のジョブを終了してから、次のジョブをキューから取得する必要があります。

または、ある程度の時間まで時間を増やすこともできます。

0
Asad Khan