1. 概要

このチュートリアルでは、Spring@Scheduledアノテーションを使用してタスクを構成およびスケジュールする方法を説明します。

@Scheduledでメソッドにアノテーションを付けるために従う必要のある簡単なルールは次のとおりです。

  • メソッドは通常、voidの戻り型を持つ必要があります(そうでない場合、戻り値は無視されます)
  • メソッドはパラメータを期待してはなりません

2. スケジューリングのサポートを有効にする

Springでスケジューリングタスクと@Scheduledアノテーションのサポートを有効にするために、Javaのenableスタイルのアノテーションを使用できます。

@Configuration
@EnableScheduling
public class SpringConfig {
    ...
}

逆に、XMLでも同じことができます。

<task:annotation-driven>

3. 固定遅延でタスクをスケジュールする

一定の遅延後に実行するようにタスクを構成することから始めましょう。

@Scheduled(fixedDelay = 1000)
public void scheduleFixedDelayTask() {
    System.out.println(
      "Fixed delay task - " + System.currentTimeMillis() / 1000);
}

この場合、最後の実行が終了してから次の実行が開始されるまでの期間は固定されています。 タスクは常に前のタスクが終了するまで待機します。

このオプションは、再度実行する前に前の実行を完了する必要がある場合に使用する必要があります。

4. 固定レートでタスクをスケジュールする

次に、一定の時間間隔でタスクを実行してみましょう。

@Scheduled(fixedRate = 1000)
public void scheduleFixedRateTask() {
    System.out.println(
      "Fixed rate task - " + System.currentTimeMillis() / 1000);
}

このオプションは、タスクの各実行が独立している場合に使用する必要があります。

デフォルトでは、スケジュールされたタスクは並行して実行されないことに注意してください。 したがって、 fixedRate を使用した場合でも、前のタスクが完了するまで次のタスクは呼び出されません。

スケジュールされたタスクで並列動作をサポートする場合は、@Asyncアノテーションを追加する必要があります。

@EnableAsync
public class ScheduledFixedRateExample {
    @Async
    @Scheduled(fixedRate = 1000)
    public void scheduleFixedRateTaskAsync() throws InterruptedException {
        System.out.println(
          "Fixed rate task async - " + System.currentTimeMillis() / 1000);
        Thread.sleep(2000);
    }

}

これで、前のタスクが実行されていなくても、この非同期タスクが毎秒呼び出されます。

5. 固定レートと固定遅延

Springの@Scheduledアノテーションを使用してスケジュールされたタスクを実行できますが、プロパティ fixedDelayおよびfixedRate、に基づいて実行の性質が変わります。

fixedDelay プロパティは、タスクの実行の終了時刻と次のタスクの実行の開始時刻の間にnミリ秒の遅延があることを確認します。

このプロパティは、タスクの1つのインスタンスのみが常に実行されるようにする必要がある場合に特に役立ちます。 扶養家族の仕事には、それは非常に役に立ちます。

fixedRateプロパティは、nミリ秒ごとにスケジュールされたタスクを実行します。タスクの以前の実行をチェックしません。

これは、タスクのすべての実行が独立している場合に役立ちます。 メモリとスレッドプールのサイズを超えることが予想されない場合は、fixedRateが非常に便利です。

ただし、着信タスクがすぐに終了しない場合は、「メモリ不足の例外」が発生する可能性があります。

6. 初期遅延のあるタスクをスケジュールする

次に、遅延(ミリ秒単位)でタスクをスケジュールしましょう。

@Scheduled(fixedDelay = 1000, initialDelay = 1000)
public void scheduleFixedRateWithInitialDelayTask() {
 
    long now = System.currentTimeMillis() / 1000;
    System.out.println(
      "Fixed rate task with one second initial delay - " + now);
}

この例では、fixedDelayinitialDelayの両方を使用していることに注意してください。 タスクは、 initialDelay 値の後に初めて実行され、fixedDelayに従って実行され続けます。

このオプションは、タスクに完了する必要のあるセットアップがある場合に便利です。

7. cron式を使用してタスクをスケジュールする

遅延とレートが十分でない場合があり、タスクのスケジュールを制御するためにcron式の柔軟性が必要です。

@Scheduled(cron = "0 15 10 15 * ?")
public void scheduleTaskUsingCronExpression() {
 
    long now = System.currentTimeMillis() / 1000;
    System.out.println(
      "schedule tasks using cron jobs - " + now);
}

この例では、毎月15日の午前10時15分に実行されるタスクをスケジュールしていることに注意してください。

デフォルトでは、Springはサーバーのローカルタイムゾーンをcron式に使用します。 ただし、ゾーン属性を使用してこのタイムゾーンを変更できます

@Scheduled(cron = "0 15 10 15 * ?", zone = "Europe/Paris")

この構成では、Springは、パリ時間の毎月15日の午前10時15分に実行されるように注釈付きメソッドをスケジュールします。

8. スケジュールのパラメータ化

これらのスケジュールのハードコーディングは簡単ですが、通常、アプリ全体を再コンパイルおよび再デプロイせずにスケジュールを制御できる必要があります。

Spring式を使用してタスクの構成を外部化し、これらをプロパティファイルに保存します。

fixedDelay タスク:

@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds}")

fixedRate タスク:

@Scheduled(fixedRateString = "${fixedRate.in.milliseconds}")

cron 式ベースのタスク:

@Scheduled(cron = "${cron.expression}")

9. XMLを使用したスケジュールされたタスクの構成

Springは、スケジュールされたタスクを構成するXMLの方法も提供します。 これらを設定するためのXML構成は次のとおりです。

<!-- Configure the scheduler -->
<task:scheduler id="myScheduler" pool-size="10" />

<!-- Configure parameters -->
<task:scheduled-tasks scheduler="myScheduler">
    <task:scheduled ref="beanA" method="methodA" 
      fixed-delay="5000" initial-delay="1000" />
    <task:scheduled ref="beanB" method="methodB" 
      fixed-rate="5000" />
    <task:scheduled ref="beanC" method="methodC" 
      cron="*/5 * * * * MON-FRI" />
</task:scheduled-tasks>

10. 実行時に動的に遅延またはレートを設定する

通常、 @Scheduled アノテーションのすべてのプロパティは、Springコンテキストの起動時に一度だけ解決および初期化されます。

したがって、Spring で@Scheduledアノテーションを使用する場合、実行時にfixedDelayまたはfixedRate値を変更することはできません。

ただし、回避策があります。 SpringのSchedulingConfigurerを使用すると、遅延またはレートを動的に設定する機会を提供する、よりカスタマイズ可能な方法が提供されます

Spring構成DynamicSchedulingConfigを作成し、SchedulingConfigurerインターフェイスを実装しましょう。

@Configuration
@EnableScheduling
public class DynamicSchedulingConfig implements SchedulingConfigurer {

    @Autowired
    private TickService tickService;

    @Bean
    public Executor taskExecutor() {
        return Executors.newSingleThreadScheduledExecutor();
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
        taskRegistrar.addTriggerTask(
          new Runnable() {
              @Override
              public void run() {
                  tickService.tick();
              }
          },
          new Trigger() {
              @Override
              public Date nextExecutionTime(TriggerContext context) {
                  Optional<Date> lastCompletionTime =
                    Optional.ofNullable(context.lastCompletionTime());
                  Instant nextExecutionTime =
                    lastCompletionTime.orElseGet(Date::new).toInstant()
                      .plusMillis(tickService.getDelay());
                  return Date.from(nextExecutionTime);
              }
          }
        );
    }

}

お気づきのとおり、 ScheduledTaskRegistrar#addTriggerTask メソッドを使用して、RunnableタスクとTrigger実装を追加して、nextExecutionTime[を再計算できます。 X204X]各実行の終了後。

さらに、DynamicSchedulingConfig@EnableSchedulingの注釈を付けて、スケジューリングを機能させます。

その結果、 TickService#tick メソッドをスケジュールして、getDelayメソッドによって実行時に動的に決定される各遅延量の後に実行しました。

11. タスクを並行して実行する

デフォルトでは、Springはローカルのシングルスレッドスケジューラを使用してタスクを実行します。 その結果、複数の @Scheduled メソッドがある場合でも、それぞれがスレッドが前のタスクの実行を完了するのを待つ必要があります。

タスクが本当に独立している場合は、それらを並行して実行する方が便利です。 そのためには、ニーズにより適したTaskSchedulerを提供する必要があります。

@Bean
public TaskScheduler  taskScheduler() {
    ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    threadPoolTaskScheduler.setPoolSize(5);
    threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
    return threadPoolTaskScheduler;
}

上記の例では、 TaskScheduler をプールサイズ5で構成しましたが、実際の構成は特定のニーズに合わせて微調整する必要があることに注意してください。

11.1. Spring Bootを使用する

Spring Bootを使用すると、さらに便利なアプローチを使用して、スケジューラーのプールサイズを増やすことができます。

spring.task.scheduling.pool.sizeプロパティを設定するだけで十分です。 spring.task.scheduling.pool.size=5

12. 結論

この記事では、@Scheduledアノテーション構成して使用する方法について説明しました。

スケジューリングを有効にするプロセスと、スケジューリングタスクパターンを構成するさまざまな方法について説明しました。 また、遅延とレートを動的に構成するための回避策も示しました。

上記の例は、GitHubにあります。