技術メモ

技術メモ

ラフなメモ

Springの非同期処理機構を試す

Springの非同期処理を試してみます。まずは公式チュートリアルを試しました。

https://spring.io/guides/gs/async-method/

非同期機構の概要がつかめて良いです。

@Async アノテーションを付与したメソッドは非同期処理となり、mainスレッドとは別スレッドで動作することになります。

同時に @EnableAsync を付与する必要がありますが、 @Configuration クラスに付与するとSpringアプリケーション全体で非同期処理機構が有効になります。

@Async を付与した関数の返り値には Future<T> ないしは void にする必要があります。

実装例

@Async で非同期処理をするサービスクラスの実装例です。

MyAsyncTask.java

@Slf4j
public class MyAsyncTask {

  @Async
  public void doTask(String id) {
    log.debug("Thread: {}, Id: {}", Thread.currentThread().getName(), id);
  }

  @Async
  public Future<String> doAsyncTaskWithResults(Integer id) {

    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    return CompletableFuture
        .completedFuture(String.format("Thread: %s, Id: %d", Thread.currentThread().getName(), id));
  }

}

続いてConfigurationクラスです。今回はJava Configクラスを用いてBean定義をします。

TaskExecutor のクラスはデフォルトでは SimpleAsyncTaskExecutor になります。これはデフォルトでは @Async の呼び出しごとにスレッドが生成され、スレッド数の制限がありません。今回はスレッドプールを用いることにするため、ThreadPoolTaskExecutor をBean定義に追加しておきます。

@EnableAsync アノテーションを付与してあります。

AsyncConfiguration.java

@Configuration
@EnableAsync
@ComponentScan
public class AsyncConfiguration {

  @Bean
  public MyAsyncTask myAsyncTask() {
    return new MyAsyncTask();
  }

  @Bean
  public Executor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(3);
    executor.initialize();
    return executor;
  }

}

mainクラスです。DIコンテナから AsyncConfiguration クラスのBeanを取得して、非同期メソッドを実行します。非同期処理の結果は List<Future<T>> になります。

@Slf4j
public class AsyncTest {

  public static void main(String[] args) throws InterruptedException {
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(AsyncConfiguration.class);
    MyAsyncTask task = context.getBean(MyAsyncTask.class);

    List<Future<String>> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
      list.add(task.doAsyncTaskWithResults(i));
    }

    for (int i = 0; i < 10; i++) {
      try {
        log.debug(list.get(i).get());
      } catch (ExecutionException e) {
        e.printStackTrace();
      }
    }

    context.close();
  }

}

実行結果

同時最大実行数が setCorePoolSize になっていることが分かります。

22:36:16.314 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4c75cab9
22:36:16.323 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
22:36:16.437 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
22:36:16.438 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
22:36:16.440 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
22:36:16.441 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
22:36:16.442 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAsyncAnnotationProcessor'
22:36:16.443 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.scheduling.annotation.ProxyAsyncConfiguration'
22:36:16.488 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'asyncConfiguration'
22:36:16.493 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myAsyncTask'
22:36:16.517 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'taskExecutor'
22:36:16.520 [main] INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Initializing ExecutorService
22:36:16.522 [main] INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Initializing ExecutorService 'taskExecutor'
22:36:18.553 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-1, Id: 0
22:36:18.553 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-2, Id: 1
22:36:18.553 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-3, Id: 2
22:36:20.554 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-2, Id: 3
22:36:20.554 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-3, Id: 4
22:36:20.554 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-1, Id: 5
22:36:22.555 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-3, Id: 6
22:36:22.555 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-1, Id: 7
22:36:22.555 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-2, Id: 8
22:36:24.556 [main] DEBUG sample.spring.async.sample1.AsyncTest - Thread: taskExecutor-2, Id: 9
22:36:24.556 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4c75cab9, started on Sun Jun 02 22:36:16 JST 2019
22:36:24.557 [main] INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Shutting down ExecutorService 'taskExecutor'

まずはこんなところです。

参考