Spring

Spring Bootで@Asyncを使って非同期処理を行う方法

Spring Bootで非同期処理を行う方法をメモしておきます。Spring Bootでは、非同期処理用のアノテーション@Asyncが用意されているので、非同期処理が比較的簡単に実装できます。

環境

- Java 8
- Gradle 6.0.1
- Spring Boot 2.2.2.RELEASE
view raw env.sh hosted with ❤ by GitHub

Spring Bootで@Asyncを使って非同期処理を行う方法

非同期処理を試すために、簡単なコマンドラインアプリケーションを作成してみます。Spring Bootでコマンドラインアプリケーションを作成する方法については、以下記事をご覧ください。
Spring Bootで簡単なコマンドラインアプリケーションを作成してみる - Reasonable Code

それではさっそく作っていきましょう。

build.gradleについては、特に非同期処理用のなにかを追加する必要はありません。

build.gradle

plugins {
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}

スレッドプールの設定を行う

以下は非同期処理のスレッドプールの設定を行うクラス(AsyncConfig.java)です。Bean登録して利用します。

AsyncConfig.java

@Configuration
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// スレッドプール数
executor.setCorePoolSize(10);
return executor;
}
}

非同期処理を実装する

以下が非同期処理を行うクラス(DemoService.java)です。非同期処理を行いたいクラスやメソッドに@Asyncを付与します。簡単ですね。

戻り値のCompletableFutureについては、呼び出し元で非同期処理のハンドリング(非同期処理の戻り値を取得する、複数の非同期処理の待ち合わせを行う、など)を行うために利用します。もともとの戻り値をCompletableFutureでラップしてあげればOKです。

DemoService.java

@Service
public class DemoService {
@Async
public CompletableFuture<Void> printName(final String name) throws InterruptedException {
System.out.println("print start: " + name);
Thread.sleep(1000); // 1秒待つ
System.out.println("print end: " + name);
return CompletableFuture.completedFuture(null);
}
}

以下が非同期処理を呼び出すクラス(DemoApplication.java)です。非同期処理を有効化するために、アプリケーションクラスに@EnableAsyncを付与します。以下の例だと、demoService.printName()をそれぞれ非同期で呼び出しています。

DemoApplication.java

@SpringBootApplication
@EnableAsync
public class DemoApplication implements CommandLineRunner {
public static void main(String[] args) {
// .close()をつけないと非同期処理が完了してもアプリケーションが終了しない
SpringApplication.run(DemoApplication.class, args).close();
}
private final DemoService demoService;
public DemoApplication(final DemoService demoService) {
this.demoService = demoService;
}
@Override
public void run(String... args) throws InterruptedException {
// それぞれ非同期で処理する
CompletableFuture<Void> name1 = demoService.printName("Alice");
CompletableFuture<Void> name2 = demoService.printName("Bob");
CompletableFuture<Void> name3 = demoService.printName("Chris");
// 非同期処理が完了するまで待つ
CompletableFuture.allOf(name1, name2, name3).join();
System.out.println("DONE!!!");
}
}

最後にアプリケーションを実行してみます。非同期で処理されているのがわかりますね。

$ ./gradlew bootRun
...
print start: Bob
print start: Chris
print start: Alice
print end: Alice
print end: Bob
print end: Chris
DONE!!!
view raw result.sh hosted with ❤ by GitHub

まとめ

Spring Bootで@Asyncを使って非同期処理を行う方法でした。ポイントをまとめます。

  • 非同期処理を行うクラスやメソッドに@Asyncを付与する
  • アプリケーションクラスに@EnableAsyncを付与する
  • CompletableFutureを利用して非同期処理のハンドリングを行う
  • スレッドプールの設定クラスをBean登録する

参考リンク

-Spring

S