Kotlin > @Async (非同期処理)




`@Async`を本番使用してはいけないという話もあり、要調査
[Kotlin coroutines and spring framework](https://www.slideshare.net/debop/kotlin-coroutines-and-spring-framework)


# @Async



## アプリケーションクラス

* EnableAsyncをimport追加
* @EnableAsyncアノテーションを追加

```kotlin:Application.kt
import org.springframework.scheduling.annotation.EnableAsync


@SpringBootApplication
@EnableAsync
class DemoApplication
```


## コントローラー

関数名に@Asyncアノテーションをつける。
これだけで非同期に動作する。

```kotlin:HelloController.kt
package com.example.demo


import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Async
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import java.util.concurrent.TimeUnit

@RestController
class HelloController {

    val logger = LoggerFactory.getLogger(this::class.java.name)

    @GetMapping("/hello")
    @Async
    fun hello(): String {
        logger.info("Standard Task Start")
        TimeUnit.SECONDS.sleep(5)
        logger.info("Standard Task End")
        return "Hello! World!"
    }
}
```



ターミナルで以下を実行

```shell
curl http://localhost:8080/hello
curl http://localhost:8080/hello
curl http://localhost:8080/hello
```

以下のような結果が返ってくる

```shell
2019-07-12 16:18:47.832  INFO 50509 --- [         task-1] com.example.demo.HelloController         : Standard Task Start
2019-07-12 16:18:47.858  INFO 50509 --- [         task-2] com.example.demo.HelloController         : Standard Task Start
2019-07-12 16:18:47.878  INFO 50509 --- [         task-3] com.example.demo.HelloController         : Standard Task Start
2019-07-12 16:18:52.837  INFO 50509 --- [         task-1] com.example.demo.HelloController         : Standard Task End
2019-07-12 16:18:52.862  INFO 50509 --- [         task-2] com.example.demo.HelloController         : Standard Task End
2019-07-12 16:18:52.881  INFO 50509 --- [         task-3] com.example.demo.HelloController         : Standard Task End
```



# 非同期処理をコントローラーからサービス側へ移動する


```kotlin:HelloController.kt
package com.example.demo

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class HelloController {

    @Autowired
    private lateinit var service: HelloService

    @GetMapping("/hello")
    fun hello()= service.standardTask()

}
```


HelloService.ktを作成

```kotlin:HelloService.kt

package com.example.demo

import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service
import java.util.concurrent.TimeUnit

@Service
class HelloService {

    val logger = LoggerFactory.getLogger(this::class.java.name)

    @Async
    fun standardTask() {
        logger.info("Standard Task Start")
        TimeUnit.SECONDS.sleep(5)
        logger.info("Standard Task End")
    }
}

```



# スレッド制御

@Asyncだけではコールされる毎にスレッドが作成される。
デフォルトでは、スレッド数の制限がない。

スレッド制御のために、TaskExecutorが使える。



```kotlin:Application.kt
@EnableAsync
class DemoApplication

↓

@EnableAsync
class DemoApplication{

	@Bean
	fun normalTaskExecutor(): TaskExecutor = ThreadPoolTaskExecutor().apply {
		corePoolSize = 1
		setQueueCapacity(5)
		maxPoolSize = 1
		setThreadNamePrefix("NormalThread-")
		setWaitForTasksToCompleteOnShutdown(true)
	}
}

```



@Asyncに関数名を追加

```kotlin:HelloService.kt
    @Async
    fun standardTask() {
    
        ↓
    
    @Async("normalTaskExecutor")
    fun standardTask() {
```

---

* corePoolSize: アイドルであってもプール内に維持されるスレッドの数
* setQueueCapacity: 

* maximumPoolSize: プール内で可能なスレッドの最大数
* keepAliveTime: スレッドの数がコアよりも多い場合、これは超過したアイドル状態のスレッドが新しいタスクを待機してから終了するまでの最大時間
* unit: keepAliveTime 引数の時間単位
* workQueue: タスクが超過するまで保持するために使用するキュー。このキューは、execute メソッドで送信された Runnable タスクだけを保持する
* setWaitForTasksToCompleteOnShutdown: 
* setThreadNamePrefix: 


キューがいっぱいにならないとcorePoolSize からmaximumPoolSizeまでスレッドが生成されない。




---


[Spring 非同期タスクの同時実行数を制限する - Qiita](https://qiita.com/kimullaa/items/59546ea68681673bd7e9)


corePoolSizeまでThreadを作る
corePoolSizeが一杯になるとqueueCapacityまでキューイングする
queueCapacityを越えるとmaxPoolSizeまでThreadを増やす
maxPoolSizeを越えるとrejectされる


---





# 参考
special thanks

[ことりんと一緒 Springもね - 6. 非同期処理 - Qiita](https://qiita.com/shinyay/items/5ad1e60435dfa9818c27#%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%87%A6%E7%90%86%E3%81%AE%E6%9C%89%E5%8A%B9%E5%8C%96)



[ThreadPoolTaskExecutor の挙動 - Qiita](https://qiita.com/hikoma/items/ffe4db14c4ac7f85616e)



[Spring 非同期タスクの同時実行数を制限する - Qiita](https://qiita.com/kimullaa/items/59546ea68681673bd7e9)


[BLOG.IK.AM - 誤解しがちなThreadPoolTaskExecutorの設定](https://blog.ik.am/entries/443)