Try using it in your preferred language.

English

  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar
translation

Ceci est un post traduit par IA.

제이온

[Spring] Utilisation de @Async

Choisir la langue

  • Français
  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar

Texte résumé par l'IA durumis

  • Ce document explique comment implémenter le traitement asynchrone Java en utilisant Spring @Async, ainsi que ses avantages et ses précautions.
  • En utilisant Spring @Async, vous pouvez implémenter facilement le traitement asynchrone en ajoutant l'annotation @EnableAsync et en plaçant l'annotation @Async sur les méthodes que vous souhaitez traiter de manière asynchrone.
  • La configuration du pool de threads permet une gestion efficace des threads, et vous pouvez utiliser Future, ListenableFuture et CompletableFuture en fonction du type de retour pour renvoyer les résultats du traitement asynchrone.

Traitement asynchrone Java

Avant d'examiner Spring @Async, il est essentiel de comprendre les concepts de synchronie, d'asynchronicité et de multithreading. En supposant que vous connaissiez ces concepts, examinons la façon de procéder au traitement asynchrone pur Java à l'aide du code. Si vous êtes familier avec les threads Java, vous pouvez ignorer ce chapitre.


public class MessageService {

    public void print(String message) {
        System.out.println(message);
    }
}

public class Main {

    public static void main(String[] args) {
        MessageService messageService = new MessageService();

        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }


Si vous créez une fonction qui reçoit un message et l'affiche simplement de manière synchrone, vous pouvez écrire le code comme ci-dessus. Si vous le modifiez en mode asynchrone multithread, vous pouvez écrire le code source comme suit.


public class MessageService {

    public void print(String message) {
        new Thread(() -> System.out.println(message))
                .start();
    }
}

public class Main {

    public static void main(String[] args) {
        MessageService messageService = new MessageService();

        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }


Cependant, cette méthode est très dangereuse car elle ne permet pas de gérer les threads. Par exemple, si 10 000 appels sont effectués simultanément, 10 000 threads doivent être créés en très peu de temps. La création de threads n'est pas bon marché et peut avoir un impact négatif sur les performances du programme, voire provoquer une erreur OOM. Par conséquent, vous devez implémenter un pool de threads pour gérer les threads, et Java fournit la classe ExecutorService.


public class MessageService {

    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void print(String message) {
        executorService.submit(() -> System.out.println(message));
    }
}

public class Main {

    public static void main(String[] args) {
        MessageService messageService = new MessageService();

        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }


Le nombre total de threads est limité à 10, et nous pouvons maintenant effectuer le traitement asynchrone multithread que nous souhaitons correctement. Cependant, cette méthode nécessite l'application de la méthode submit() d'ExecutorService à chaque méthode que nous souhaitons traiter de manière asynchrone, ce qui nécessite des modifications répétitives. En d'autres termes, si vous souhaitez convertir une méthode écrite initialement de manière synchrone en mode asynchrone, vous devez nécessairement modifier la logique de la méthode elle-même.


Spring @Async

Méthode simple

@EnableAsync
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

@Service
public class MessageService {

    @Async
    public void print(String message) {
        System.out.println(message);
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() {
        for (int i = 1; i <= 100; i++) {
            messageService.print(i + "");
        }
    }


Il suffit d'ajouter l'annotation @EnableAsync au-dessus de la classe Application et l'annotation @Async au-dessus de la méthode de logique synchrone que vous souhaitez traiter de manière asynchrone. Cependant, cette méthode ne gère pas les threads. En effet, la configuration par défaut de @Async est d'utiliser SimpleAsyncTaskExecutor, qui n'est pas un pool de threads, mais simplement une fonction de création de threads.


Utilisation d'un pool de threads

Tout d'abord, supprimez @EnableAsync de la classe Application. Si la classe Application est configurée avec @EnableAutoConfiguration ou @SpringBootApplication, elle lira les informations sur le bean threadPoolTaskExecutor de la classe SpringAsyncConfig (à créer plus tard) configurée au moment de l'exécution.


@Configuration
@EnableAsync // Application이 아닌, Async 설정 클래스에 붙여야 함.
public class SpringAsyncConfig {

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3); // 기본 스레드 수
        taskExecutor.setMaxPoolSize(30); // 최대 스레드 수
        taskExecutor.setQueueCapacity(100); // Queue 사이즈
        taskExecutor.setThreadNamePrefix("Executor-");
        return taskExecutor;
    }


Vous pouvez définir les tailles de core et de max. Dans ce cas, vous vous attendez à ce que le pool de threads fonctionne avec la taille de core initiale, puis que le nombre de threads augmente jusqu'à la taille de max s'il n'est pas possible de traiter davantage de tâches. Cependant, ce n'est pas le cas.


En interne, une file d'attente LinkedBlockingQueue de taille Integer.MAX_VALUE est créée. Si les threads core ne peuvent pas traiter les tâches, ils attendent dans la file d'attente. Lorsque la file d'attente est pleine, des threads sont créés jusqu'à la taille de max pour traiter les tâches.


Si vous ne souhaitez pas définir la taille de la file d'attente sur Integer.MAX_VALUE, vous pouvez définir queueCapacity. Si vous définissez les paramètres comme ci-dessus, le pool de threads fonctionnera avec 3 threads initialement. Si la vitesse de traitement est lente, les tâches sont placées dans une file d'attente de 100 éléments. Si des demandes supplémentaires sont reçues, un maximum de 30 threads sont créés pour traiter les tâches.


Une fois la configuration du pool de threads terminée, il suffit d'ajouter le nom du bean à la méthode annotée @Async.


@Service
public class MessageService {

    @Async("threadPoolTaskExecutor")
    public void print(String message) {
        System.out.println(message);
    }


Si vous souhaitez définir plusieurs types de pools de threads, vous pouvez créer plusieurs méthodes de création de beans telles que threadPoolTaskExecutor(), et insérer le bean de pool de threads que vous souhaitez lors de la configuration de @Async.


Forme de retour en fonction du type de retour

Cas où il n'y a pas de valeur de retour

C'est le cas où la méthode à traiter de manière asynchrone n'a pas besoin de renvoyer de résultat de traitement. Dans ce cas, vous devez définir le type de retour de l'annotation @Async sur void.


Cas où il y a une valeur de retour

Vous pouvez utiliser les types Future, ListenableFuture et CompletableFuture comme type de retour. Le format de retour de la méthode asynchrone peut être enveloppé dans new AsyncResult().


[Future]

@Service
public class MessageService {

    @Async
    public Future print(String message) throws InterruptedException {
        System.out.println("Task Start - " + message);
        Thread.sleep(3000);
        return new AsyncResult<>("jayon-" + message);
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() throws ExecutionException, InterruptedException {
        for (int i = 1; i <= 5; i++) {
            Future future = messageService.print(i + "");
            System.out.println(future.get());
        }
    }
}

// 실행 결과
Task Start - 1
jayon-1
Task Start - 2
jayon-2
Task Start - 3
jayon-3
Task Start - 4
jayon-4
Task Start - 5


future.get() joue un rôle de blocage en attendant que le résultat de la requête arrive. Cela devient donc une méthode de blocage asynchrone, ce qui n'est pas performant. En général, Future n'est pas utilisé.


[ListenableFuture]

@Service
public class MessageService {

    @Async
    public ListenableFuture print(String message) throws InterruptedException {
        System.out.println("Task Start - " + message);
        Thread.sleep(3000);
        return new AsyncResult<>("jayon-" + message);
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            ListenableFuture listenableFuture = messageService.print(i + "");
            listenableFuture.addCallback(System.out::println, error -> System.out.println(error.getMessage()));
        }
    }
}

// 실행 결과
Task Start - 1
Task Start - 3
Task Start - 2
jayon-1
jayon-2
Task Start - 5
jayon-3
Task Start - 4
jayon-4


ListenableFuture permet de traiter les tâches de manière non bloquante via des rappels. Le premier paramètre de la méthode addCallback() définit la méthode de rappel de fin de tâche, et le second paramètre définit la méthode de rappel d'échec de tâche. Notez que la taille du thread core du pool de threads est définie sur 3, vous pouvez donc voir que les messages « Task Start » sont affichés 3 fois au début.



[CompletableFuture]

ListenableFuture permet de mettre en œuvre une logique non bloquante, mais si un rappel dans un rappel est nécessaire, cela peut entraîner du code très complexe appelé « hell of callbacks ».


Untitled


Bien sûr, étant donné que nous n'allons pas aborder CompletableFuture en détail dans ce tutoriel, si vous êtes curieux de savoir comment gérer les rappels complexes, veuillez consulter le lien source ci-dessous.


@Service
public class MessageService {

    @Async
    public CompletableFuture print(String message) throws InterruptedException {
        System.out.println("Task Start - " + message);
        Thread.sleep(3000);
        return new AsyncResult<>("jayon-" + message).completable();
    }
}

@RequiredArgsConstructor
@RestController
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/messages")
    @ResponseStatus(code = HttpStatus.OK)
    public void printMessage() throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            CompletableFuture completableFuture = messageService.print(i + "");
            completableFuture
                    .thenAccept(System.out::println)
                    .exceptionally(error -> {
                        System.out.println(error.getMessage());
                        return null;
                    });
        }
    }


La lisibilité est meilleure que celle des rappels de ListenableFuture, et elle exécute parfaitement les fonctionnalités non bloquantes. Par conséquent, il est recommandé d'utiliser CompletableFuture lorsque vous utilisez @Async et que vous avez besoin d'une valeur de retour.


Avantages de @Async

Les développeurs peuvent écrire des méthodes de manière synchrone et, s'ils souhaitent une méthode asynchrone, il suffit d'ajouter l'annotation @Async au-dessus de la méthode. Vous pouvez donc créer du code qui est bien entretenu en termes de synchronie et d'asynchronicité.


Points à noter concernant @Async

Pour utiliser la fonction @Async, vous devez déclarer l'annotation @EnableAsync. Si vous ne configurez pas d'autres paramètres, elle fonctionne en mode proxy. En d'autres termes, toutes les méthodes asynchrones qui fonctionnent avec l'annotation @Async suivent les contraintes de Spring AOP. Pour plus d'informations, consultezthis post.


  • Si vous ajoutez @Async à une méthode privée, AOP ne fonctionne pas.
  • Si des méthodes dans le même objet s'appellent mutuellement, AOP ne fonctionne pas.


Source

제이온
제이온
제이온
제이온
[Java] Synchronized Collection vs Concurrent Collection Dans cet article, nous avons analysé les différentes méthodes et les avantages et les inconvénients pour résoudre les problèmes de synchronisation lors de l'utilisation de collections dans un environnement multithread en Java. Nous avons présenté les cara

25 avril 2024

[Spring] Qu'est-ce que Filter, Interceptor et Argument Resolver ? Découvrez en profondeur le concept et les différences entre Filter, Interceptor et Argument Resolver dans une application Web Spring. Comprendre les méthodes d'implémentation de chaque fonctionnalité, leur moment d'utilisation, leurs avantages et leurs in

27 avril 2024

[Effective Java] Évitez la création d'objets inutiles Ce guide vous explique comment réduire la création d'objets inutiles en Java. Pour les objets immuables comme String et Boolean, il est préférable d'utiliser des littéraux. Il est également conseillé de mettre en cache les instances Pattern pour les expre

28 avril 2024

[Concurrency] Opération atomique : Clôture de mémoire et ordre de mémoire Ce billet de blog explique comment prendre en compte l'ordre de la mémoire dans les opérations atomiques et l'importance des options d'ordre. Il fournit une description des diverses options d'ordre, telles que Relaxed, Acquire, Release, AcqRel et SecCst,
곽경직
곽경직
곽경직
곽경직
곽경직

12 avril 2024

[Histoire d'un développeur SI] 03. Préparation de l'entretien d'embauche dans une entreprise SI L'entretien d'embauche pour les développeurs SI met davantage l'accent sur la capacité de codage que sur les compétences techniques élevées. Ainsi, la compréhension de la structure Spring + mybatis apprise dans les écoles de formation subventionnées par l
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

16 avril 2024

[Non-majors, Surviving as Developers] 14. Résumé des questions d'entrevue technique fréquemment posées aux développeurs débutants Guide de préparation aux entrevues techniques pour les développeurs débutants. Zone de mémoire principale, structures de données, RDBMS et NoSQL, programmation procédurale et orientée objet, surcharge et surcharge, algorithmes de remplacement de page, pro
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

3 avril 2024

Structure de base du trading automatique d'actions (mise à jour en cours...) Ce guide explique en détail le processus de développement d'un programme de trading automatique d'actions, étape par étape, de l'ouverture d'un compte à la préparation de l'environnement de développement, en passant par l'intégration de l'API, la concepti
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

22 avril 2024

Comment Rust empêche les bogues de concurrence Rust est un langage puissant qui résout les défis de la programmation concurrente. Son système de types et son modèle de propriété garantissent la sécurité du transfert et du partage de données entre les threads. Les modèles de mutabilité interne, tels qu
곽경직
곽경직
곽경직
곽경직
곽경직

28 mars 2024

Enregistrement des difficultés rencontrées lors du développement de l'API de Korea Investment Securities Cet article de blog est destiné aux développeurs qui souhaitent créer un programme de trading automatisé à l'aide de l'API de Korea Investment Securities. Il présente les difficultés rencontrées lors du processus de développement, telles que l'ouverture d
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

23 avril 2024