@Async
での非同期処理において、ThreadPoolTaskExecutor
をすれば同時実行数の制御が可能となります。
しかし、重要な処理と大して重要でない処理を同じThreadPoolで管理してしまうと、重要でない処理が大量に実行待ち状態になったとき、重要な処理を実行しようとしても、なかなか実行できないといった問題が発生します。
そこで、処理によって利用するTaskExecutorを分けて、別のThreadPoolで管理させようと思います。
実装方法
TaskExecutorを2つBean定義する。
どちらもThreadPoolTaskExecutorで、同時実行可能数は1にしています。
@SpringBootApplication
@EnableAsync
public class TaskApplication {
public static void main(String[] args) {
SpringApplication.run(TaskApplication.class, args);
}
@Bean
TaskExecutor importantTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(5);
return executor;
}
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(5);
return executor;
}
}
非同期処理を行う部分は以下のように実装します。(インターフェースは省略)
@Async
で利用するTaskExecutorの名前を指定します。
処理の中で、10秒間のスリープを挟むことによって、実行待ち状態が発生するようにしています。
@Service
@Slf4j
public class TaskServiceImpl implements TaskService {
@Async("importantTaskExecutor")
@Override
public void importantTask() {
try {
log.info("start important task");
TimeUnit.SECONDS.sleep(10);
log.info("end important task");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Async("taskExecutor")
@Override
public void task() {
try {
log.info("start normal task");
TimeUnit.SECONDS.sleep(10);
log.info("end normal task");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以下のコードで動作を確認しました。
@RestController
@RequiredArgsConstructor
public class TaskAsyncController {
private final TaskService taskService;
@GetMapping("/")
public String task() {
taskService.task();
taskService.task();
taskService.task();
taskService.task();
taskService.importantTask();
return "Success!!";
}
}
実行すると、taskメソッドは1つ実行されますが、残り3つは実行待ち状態となります。
しかし、importantTaskメソッドは実行待ちにはならず、すぐ実行されます。
これで、別のThreadPoolTaskExecutorが利用できていることがわかります。