@Scheduled
アノテーションが付いた複数のコンポーネントがあり、それらが同時に実行されるようにスケジュールされている場合でも、Springは一度に1つしか起動しないことがわかります。
私のユースケースは次のとおりです。 @Scheduledアノテーションをそれぞれ独自のスレッドで実行したいのですが、スレッドごとに1回だけです。
これを考えると擬似コード 2つのスケジューラーで:
@Scheduled(cron = "0 * * * * *") //run every minute
public void methodA() {
log.info("Running method A");
executeLongRunningJob("Finished method A");
}
@Scheduled(cron = "0 * * * * *") //run every minute
public void methodB() {
log.info("Running method B");
executeLongRunningJob("Finished method B");
}
private void executeLongRunningJob(String msg) {
Thread.sleep(70 seconds);
System.out.println(msg);
}
タスクは、スケジューラーの実行がスケジュールされているよりも時間がかかることに注意してください。これは非常に重要です。実行が終了する前にスケジューラーを再開したくありません。
このコードを箱から出して実行すると、次の出力が得られます。
Running method A
Finished method A
Running method B
Finished method B
Running method A
Finished method A
Running method B
Finished method B
... and so on
したがって、明らかに、単一のスレッドで両方のスケジューラーを実行します。
高価なメソッドに@Async
を配置すると、新しいスケジューラーが開始される前に高価なメソッドが終了しないことを除いて、ほぼ正しい動作が得られます。
Running method A
Running method B
Running method A
Running method B
Finished method A
Finished method B
... and so on
私が欲しいのはこの出力です:
Running method A
Running method B
Finished method A
Finished method B
Running method A
Running method B
Finished method A
Finished method B
... and so on
どうすればこれを達成できますか?各スケジューラーを同時に実行したいのですが、完了するまで待ってから再度実行してください。 2つ以上のスケジューラーが同じ時間に実行されていることを思い出してください。
その通りです。デフォルトでは、スケジューラーはサイズ1のスレッドプールを使用するため、すべてのタスクが順番に処理されます。 TaskScheduler
Beanを目的のプールサイズで構成することで、これを実行できます。次の例を検討してください。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import Java.util.Date;
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public TaskScheduler taskScheduler() {
final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
return scheduler;
}
@Scheduled(fixedDelay = 2 * 1000L, initialDelay = 3 * 1000L)
public void scheduled1() throws InterruptedException {
System.out.println(new Date() + " " + Thread.currentThread().getName() + ": scheduled1");
Thread.sleep(1000);
}
@Scheduled(fixedDelay = 3 * 1000L, initialDelay = 3 * 1000L)
public void scheduled2() throws InterruptedException {
System.out.println(new Date() + " " + Thread.currentThread().getName() + ": scheduled2");
Thread.sleep(1000);
}
}
次に、スケジュールされたすべてのタスクを個別のスレッドで実行します。
Tue Jul 18 20:21:50 CEST 2017 taskScheduler-1: scheduled2
Tue Jul 18 20:21:50 CEST 2017 taskScheduler-2: scheduled1
Tue Jul 18 20:21:53 CEST 2017 taskScheduler-1: scheduled1
Tue Jul 18 20:21:54 CEST 2017 taskScheduler-3: scheduled2
Tue Jul 18 20:21:56 CEST 2017 taskScheduler-2: scheduled1
Tue Jul 18 20:21:58 CEST 2017 taskScheduler-4: scheduled2
Tue Jul 18 20:21:59 CEST 2017 taskScheduler-1: scheduled1