SpringBootで定期実行で非同期処理


概要

Spring Bootで定期実行と非同期処理をやってみたので備忘録です。

環境

OpenJDK 11
Spring Boot 2.2.1

定期実行

こちらを参考にしました。Spring Bootでtaskを定期実行する方法

@EnableScheduling
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@Scheduled(fixedRate = 10000)
public void scheduledTask() {
    this.doTask();
}

Scheduledアノテーションを使います。

非同期処理

Ayncアノテーションを使えば非同期処理にできます。

@EnableAsync
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
@Async
public void doTask() {
    NormalTask normalTask = new NormalTask();
    normalTask.do();
}

doTaskメソッドが呼ばれると処理は別スレッドにて実行されます。

同時にやる

この組み合わせで定期実行→非同期処理が可能だと思っていたのですが、ScheduledとAsyncの組み合わせでは非同期処理にはなりません。

@Async
public void doTask() {
    NormalTask normalTask = new NormalTask();
    normalTask.do();
}

@Scheduled(fixedRate = 10000)
public void scheduled() {
    this.doTask();
}

上の例はAsyncが効かず、normalTask.do()はブロックされます。

ScheduledアノテーションのついたメソッドはRunnableを作り別スレッドで処理を行いますが、この時にAsyncは効かなくなります。
定期実行を管理するScheduledThreadPoolExecutorはRunnableのrun()が終了するまでブロックするようです(終了から次の定期実行時間を算出するため)。

ということで、ScheduledとAsyncアノテーションの併用は不可。

解決策

Scheduledで作るRunnableの中で別スレッドを作り、その中で処理を実行すると解決できました。
Scheduledが作るRunnableは非同期処理の別スレッドを作るとすぐに終了するため問題ないのではないかと思っています。

AsyncTask.java
public class AsyncTask implements Runnable {

    @Override
    public void run() {
        // Do some task
    }
}

public void doTask() {
    AsyncTask asyncTask = new AsyncTask();
    Thread newThread = new Thread(asyncTask);
    newThread.start();
}

@Scheduled(fixedRate = 10000)
public void scheduled() {
    this.doTask();
}

以上、ご指摘等あればぜひご連絡ください。

参考

Spring Bootでtaskを定期実行する方法
using @Scheduled and @Async together?