Dans le passé, le comportement synchrone de l'exécution et de la réception des résultats du traitement des demandes était suffisant dans de nombreux endroits. Cependant, ces dernières années, avec les progrès des appareils mobiles tels que les smartphones et les processeurs multicœurs, Un traitement asynchrone qui peut effectuer plusieurs traitements en même temps est également requis.
Dans Spring, vous pouvez facilement effectuer un traitement asynchrone sur un autre thread en utilisant l'annotation @ Async.
Voyons comment créer un traitement asynchrone en utilisant @ Async.
Lorsque vous exécutez une instruction en Java, le traitement est effectué dans le thread. Dans le cas d'un thread unique, les instructions sont traitées séquentiellement sur le thread. Par conséquent, un traitement simultané n'est pas possible.
Par conséquent, préparez plusieurs threads et exécutez des instructions sur chaque thread. Exécutez l'instruction dans un autre thread sans attendre la fin pour réaliser un traitement parallèle de manière asynchrone.

Spring Dependencies
Définissez les paramètres pour activer le traitement asynchrone dans l'application Spring Boot.
Tout ce que vous avez à faire est d'ajouter l'annotation @ EnableAsync à la classe de configuration (annotation @ Configuration ou la classe avec l'annotation @ SpringBootApplication).
@SpringBootApplication
@EnableAsync
class SimpleApplication
Ajoutez l'annotation @ Async à la fonction pour laquelle vous souhaitez effectuer un traitement asynchrone dans un autre thread.
La fonction avec cette annotation @ Async est traitée comme la fonction à traiter de manière asynchrone, et l'instruction traitée est exécutée dans un autre thread.
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service
import java.util.concurrent.TimeUnit
@Service("Async Task Service")
class AsyncTaskService {
    val logger = LoggerFactory.getLogger(this::class.java.name)
    @Async
    fun standardTask() {
        logger.info("Task Start")
        TimeUnit.SECONDS.sleep(5)
        logger.info("Task End")
    }
}
Placez un contrôleur REST qui appelle la classe responsable du traitement asynchrone avec l'annotation @ Async.
Définissez le point de terminaison d'accès sur / async.
Cela permet à ce processus asynchrone d'être appelé à http: // localhost: 8080 / async.
@RestController
@RequestMapping("/async")
class AsyncTaskController {
    @GetMapping
    fun callStandardTask() = service.standardTask()
}
Appelons en fait le traitement asynchrone 5 fois de suite.
$ curl  http://localhost:8080/async
$ curl  http://localhost:8080/async
$ curl  http://localhost:8080/async
$ curl  http://localhost:8080/async
$ curl  http://localhost:8080/async
Le journal d'exécution du début et de la fin de chaque processus asynchrone est affiché dans la sortie standard comme indiqué ci-dessous.
2018-12-19 20:50:22.662  INFO 22121 --- [cTaskExecutor-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:00.545  INFO 22121 --- [cTaskExecutor-2] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:01.886  INFO 22121 --- [cTaskExecutor-3] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:04.397  INFO 22121 --- [cTaskExecutor-4] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:05.550  INFO 22121 --- [cTaskExecutor-5] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 20:51:06.887  INFO 22121 --- [cTaskExecutor-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 20:51:09.400  INFO 22121 --- [cTaskExecutor-2] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 20:52:03.338  INFO 22121 --- [cTaskExecutor-3] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 20:52:05.123  INFO 22121 --- [cTaskExecutor-4] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 20:52:06.290  INFO 22121 --- [cTaskExecutor-5] i.p.s.simple.service.AsyncTaskService    : Normal Task End
En regardant ce résultat, il peut être confirmé que 5 threads de cTaskExecutor-1 à cTaskExecutor-5 sont créés et traités, et chacun est traité de manière asynchrone.
Dans le processus asynchrone exécuté précédemment, des threads ont été créés pour le nombre d'appels. Et si cette application est appelée très souvent? Il est possible qu'un grand nombre de threads soit créé, épuisant les ressources système, réduisant les performances et, dans le pire des cas, arrêtant le système. Il existe de nombreux serveurs d'applications Java EE qui permettent de contrôler le nombre de threads en tant que plate-forme. Mais qu'en est-il d'une application Spring Boot qui n'utilise pas un serveur d'applications Java EE comme celui-ci?
Lors de l'exécution d'un traitement asynchrone dans Spring, en ajoutant l'annotation @ Async, SimpleAsyncTaskExecutor est utilisé par défaut pour créer un autre thread et effectuer un traitement asynchrone.
D'autre part, si vous voulez configurer le pool de threads pour contrôler le nombre de threads, configurez et utilisez ThreadPoolTaskExecutor.
Définissez dans la classe de configuration avec @ EnableAsync.
Ci-dessous, nous créons deux types de piscines.
@SpringBootApplication
@EnableAsync
class SimpleApplication {
    @Bean
    fun normalTaskExecutor(): TaskExecutor  = ThreadPoolTaskExecutor().apply {
        corePoolSize = 1
        setQueueCapacity(5)
        maxPoolSize = 1
        setThreadNamePrefix("NormalThread-")
        setWaitForTasksToCompleteOnShutdown(true)
    }
    @Bean
    fun prioritizedTaskExecutor(): TaskExecutor  = ThreadPoolTaskExecutor().apply {
        corePoolSize = 5
        setQueueCapacity(5)
        maxPoolSize = 5
        setThreadNamePrefix("PrioritizedThread-")
        setWaitForTasksToCompleteOnShutdown(true)
    }
}
Définissez les attributs suivants pour configurer le pool de threads.
| Nom | Contenu | 
|---|---|
| corePoolSize | Créez le nombre de threads jusqu'à cette valeur de paramètre | 
| setQueueCapacity | Lorsque le nombre de corePoolSize est dépassé, mise en file d'attente jusqu'à cette valeur de paramètre | 
| maxPoolSize | Lors de la mise en file d'attente au maximum de setQueueCapacity, le nombre de threads est créé jusqu'à cette valeur de paramètre | 

Spécifiez explicitement le nom du bean de ThreadPoolTaskExecutor avec l'annotation @ Async comme indiqué ci-dessous.
Détermine quel ThreadPoolTaskExecutor sera asynchrone.
    @Async("prioritizedTaskExecutor")
    fun prioritizedTask() {
        logger.info("Prioritized Task Start")
        TimeUnit.SECONDS.sleep(5)
        logger.info("Prioritized Task End")
    }
    @Async("normalTaskExecutor")
    fun normalTask() {
        logger.info("Normal Task Start")
        TimeUnit.SECONDS.sleep(5)
        logger.info("Normal Task End")
    }
Regardons le résultat du traitement asynchrone avec le nombre de pools de threads défini sur «1».
$ curl  http://localhost:8080/async/normal
$ curl  http://localhost:8080/async/normal
$ curl  http://localhost:8080/async/normal
$ curl  http://localhost:8080/async/normal
$ curl  http://localhost:8080/async/normal
Comme indiqué ci-dessous, vous pouvez voir que le traitement est effectué uniquement par le thread NormalThread-1.
Comme spécifié, il est traité avec le nombre de threads «1».
2018-12-19 22:30:17.654  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:22.658  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 22:30:24.437  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:29.441  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 22:30:29.441  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:34.442  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 22:30:34.443  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:39.445  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
2018-12-19 22:30:39.446  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task Start
2018-12-19 22:30:44.455  INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService    : Normal Task End
Ensuite, regardons le traitement asynchrone avec le nombre de pools de threads défini sur «5».
$ curl  http://localhost:8080/async/high
$ curl  http://localhost:8080/async/high
$ curl  http://localhost:8080/async/high
$ curl  http://localhost:8080/async/high
$ curl  http://localhost:8080/async/high
Ici, vous pouvez voir que 5 threads de HighThread-1 à HighThread-5 ont été créés et utilisés.
Comme vous pouvez le voir, vous pouvez facilement effectuer un traitement asynchrone compte tenu du contrôle du pool de threads.
2018-12-19 22:37:55.784  INFO 22746 --- [HighThread-1] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:37:57.469  INFO 22746 --- [HighThread-2] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:37:57.898  INFO 22746 --- [HighThread-3] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:37:58.956  INFO 22746 --- [HighThread-4] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:37:59.582  INFO 22746 --- [HighThread-5] i.p.s.simple.service.AsyncTaskService    : Prioritized Task Start
2018-12-19 22:38:00.787  INFO 22746 --- [HighThread-1] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
2018-12-19 22:38:02.473  INFO 22746 --- [HighThread-2] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
2018-12-19 22:38:02.900  INFO 22746 --- [HighThread-3] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
2018-12-19 22:38:03.957  INFO 22746 --- [HighThread-4] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
2018-12-19 22:38:04.586  INFO 22746 --- [HighThread-5] i.p.s.simple.service.AsyncTaskService    : Prioritized Task End
Le traitement asynchrone est un traitement qui peut être utilisé efficacement non seulement dans les applications Web, mais également dans les applications par lots. De cette manière, le traitement asynchrone peut être effectué tout en créant facilement des threads et en contrôlant le pool de threads, il semble donc qu'il existe de nombreuses situations possibles dans lesquelles il peut être utilisé.
Recommended Posts