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

Esta é uma postagem traduzida por IA.

제이온

[Spring] Como usar @Async

  • Idioma de escrita: Coreana
  • País de referência: Todos os países country-flag

Selecionar idioma

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

Texto resumido pela IA durumis

  • Explicamos como implementar o processamento assíncrono Java usando o Spring @Async, seus benefícios e precauções.
  • Usando o Spring @Async, você pode implementar o processamento assíncrono de forma simples adicionando a anotação @EnableAsync e anexando a anotação @Async ao método que você deseja processar de forma assíncrona.
  • A configuração do pool de threads permite gerenciar threads de forma eficiente, e você pode retornar os resultados do processamento assíncrono usando Future, ListenableFuture e CompletableFuture, dependendo do tipo de retorno.

Processamento Assíncrono Java

Antes de olhar para o Spring @Async, os conceitos de sincronia, asincronia e multithreading são essenciais. Assumindo que você está familiarizado com esses conceitos, vamos dar uma olhada no processamento assíncrono Java puro em código. Se você estiver familiarizado com threads Java, pode pular este capítulo.


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 + "");
        }
    }


Se você criar uma função para receber uma mensagem e simplesmente imprimí-la de forma síncrona, você pode escrever o código como acima. Se você mudar isso para um método de multithreading assíncrono, você pode escrever o código-fonte como este.


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 + "");
        }
    }


No entanto, este método é muito perigoso porque você não pode gerenciar threads. Por exemplo, se 10.000 chamadas forem feitas simultaneamente, 10.000 threads precisarão ser criadas em um período muito curto. O custo de criar threads não é pequeno, então isso afetará negativamente o desempenho do programa e pode até causar um erro OOM. Portanto, para gerenciar threads, você precisa implementar um pool de threads, e o Java fornece a 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 + "");
        }
    }


O número total de threads é limitado a 10 e podemos fazer o processamento assíncrono de multithreading que queremos corretamente. No entanto, com este método, você precisa aplicar o método submit() do ExecutorService a cada método que deseja processar de forma assíncrona, portanto você precisa fazer alterações repetitivas. Ou seja, se você quiser mudar um método escrito originalmente em lógica síncrona para assíncrono, você precisará necessariamente mudar a lógica do próprio método.


Spring @Async

Método simples

@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 + "");
        }
    }


Basta adicionar a anotação @EnableAsync acima da classe Application e a anotação @Async acima do método de lógica síncrona que você deseja processar de forma assíncrona. No entanto, este método não gerencia threads. Isso ocorre porque a configuração padrão do @Async é usar o SimpleAsyncTaskExecutor, que não é um pool de threads, mas simplesmente cria threads.


Método usando um pool de threads

Primeiro, remova o @EnableAsync da classe Application. Se a classe Application tiver @EnableAutoConfiguration ou @SpringBootApplication configurado, ele lerá as informações do bean threadPoolTaskExecutor da classe SpringAsyncConfig (que será criada abaixo) no momento da execução, pois a classe SpringAsyncConfig tem @Configuration configurado.


@Configuration
@EnableAsync // Deve ser adicionado à classe de configuração assíncrona, não à Application.
public class SpringAsyncConfig {

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3); // Número de threads padrão
        taskExecutor.setMaxPoolSize(30); // Número máximo de threads
        taskExecutor.setQueueCapacity(100); // Tamanho da fila
        taskExecutor.setThreadNamePrefix("Executor-");
        return taskExecutor;
    }


Você pode definir o tamanho do core e do máximo. Nesse caso, você esperaria que ele funcionasse com o tamanho do core inicialmente e, se não conseguir processar mais trabalhos, as threads aumentariam para o tamanho máximo, mas não é esse o caso.


Internamente, um LinkedBlockingQueue do tamanho Integer.MAX_VALUE é criado e, se o trabalho não puder ser processado pelas threads do tamanho do core, ele ficará na fila. Se a fila ficar cheia, ele criará threads do tamanho máximo para processar o trabalho.


Nesse caso, se você não se sentir confortável em definir o tamanho da fila para Integer.MAX_VALUE, você pode configurar queueCapacity. Definindo isso como acima, ele será processado pelas 3 threads inicialmente, e se a velocidade de processamento diminuir, ele colocará as tarefas na fila de tamanho 100 e, se mais solicitações chegarem, ele criará até 30 threads para processar o trabalho.


Depois que a configuração do pool de threads estiver concluída, você poderá adicionar o nome do bean ao método ao qual a anotação @Async está anexada.


@Service
public class MessageService {

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


Se você quiser configurar vários tipos de pools de threads, crie vários métodos de criação de beans como threadPoolTaskExecutor() e insira o bean de pool de threads desejado ao configurar @Async.


Formato retornado por tipo de retorno

Sem valor de retorno

Este é o caso quando o método que precisa ser processado de forma assíncrona não precisa passar o resultado do processamento. Nesse caso, você pode definir o tipo de retorno da anotação @Async como void.


Com valor de retorno

Você pode usar os tipos Future, ListenableFuture e CompletableFuture como tipo de retorno. A forma de retorno do método assíncrono pode ser encapsulada em 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());
        }
    }
}

// Resultado da execução
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() age como bloqueio para aguardar até que o resultado da solicitação chegue. Portanto, ele se torna um método de bloqueio assíncrono e tem um desempenho ruim. Normalmente, Future não é usado.


[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()));
        }
    }
}

// Resultado da execução
Task Start - 1
Task Start - 3
Task Start - 2
jayon-1
jayon-2
Task Start - 5
jayon-3
Task Start - 4
jayon-4


ListenableFuture permite que você processe trabalhos de forma não bloqueante por meio de callbacks. O primeiro parâmetro do método addCallback() é o método de callback de conclusão da tarefa e o segundo parâmetro é o método de callback de falha da tarefa. Como referência, o core do pool de threads está definido como 3, então você pode ver que a mensagem "Task Start" é impressa 3 vezes no início.



[CompletableFuture]

Embora você possa implementar lógica não bloqueante com apenas o ListenableFuture, se você precisar de um callback dentro de um callback, isso levará a um código muito complicado, conhecido como inferno de callback.


Untitled


Claro, não abordaremos o CompletableFuture em detalhes neste momento, então se você estiver interessado em lidar com callbacks complexos, consulte o link da fonte abaixo.


@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;
                    });
        }
    }


Ele é mais legível do que a definição de callback do ListenableFuture e executa completamente a função não bloqueante. Portanto, ao usar @Async, é recomendável usar o CompletableFuture se um valor de retorno for necessário.


Vantagens do @Async

Os desenvolvedores podem escrever métodos de forma síncrona e, se quiserem um método assíncrono, basta adicionar a anotação @Async acima do método. Portanto, você pode criar um código manutenível em termos de sincronia e asincronia.


Avisos sobre o @Async

Para usar a funcionalidade @Async, você precisa declarar a anotação @EnableAsync, mas se nenhuma configuração adicional for feita, ele funcionará no modo proxy. Ou seja, todos os métodos assíncronos que funcionam com a anotação @Async seguirão as restrições do Spring AOP. Para obter mais informações, consulteessa postagem.


  • Se você anexar @Async a um método privado, o AOP não funcionará.
  • Se você chamar métodos dentro do mesmo objeto, o AOP não funcionará.


Fonte

제이온
제이온
제이온
제이온
[Java] Coleção Sincronizada vs Coleção Concorrente Neste artigo, analisamos os prós e contras de diferentes métodos para resolver problemas de sincronização ao usar coleções em ambientes multithread no Java. Apresentamos as características e diferenças de desempenho das coleções sincronizadas, como Vector

25 de abril de 2024

[Spring] O que são Filter, Interceptor e Argument Resolver? Saiba mais sobre os conceitos e diferenças entre Filter, Interceptor e Argument Resolver no processamento de solicitações em aplicativos web Spring. Analise comparativamente os métodos de implementação, o momento de uso, as vantagens e desvantagens de cad

27 de abril de 2024

[Effective Java] Item 1: Considere métodos de fábrica estáticos em vez de construtores Os métodos de fábrica estáticos são uma maneira flexível e eficiente de criar instâncias em vez de construtores. Eles podem ter um nome, retornar instâncias que atendem a condições específicas e melhorar o desempenho por meio do cache. Ao contrário do pad

27 de abril de 2024

[Concorrência] Operação Atômica: Memory Fence e Memory Ordering Esta postagem de blog explica como considerar a ordem da memória em operações atômicas e a importância das opções de ordenamento. Ele explica as várias opções de ordenamento, como Relaxado, Adquirir, Lançar, AcqRel e SecCst, junto com os prós e contras de
곽경직
곽경직
곽경직
곽경직
곽경직

12 de abril de 2024

Como a Rust previne bugs de simultaneidade Rust é uma linguagem poderosa que resolve os desafios da programação concorrente. Seu sistema de tipos e modelo de propriedade garantem a transferência e o compartilhamento de dados seguros entre threads. Padrões de mutabilidade interna, como Mutex, Chann
곽경직
곽경직
곽경직
곽경직
곽경직

28 de março de 2024

Ideias para melhorar o programa de negociação automatizada Este artigo apresenta ideias para melhorar as funcionalidades do programa de negociação automatizada de Grid Trading, sugerindo a inclusão de gerenciamento de grandes eventos, lógica de gerenciamento de investimentos, funcionalidade de posição curta, entr
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

21 de abril de 2024

[Não graduado, sobrevivendo como desenvolvedor] 14. Resumo do conteúdo da entrevista técnica frequente para desenvolvedores juniores Guia de preparação para entrevista técnica para desenvolvedores juniores. Área de memória principal, estrutura de dados, RDBMS e NoSQL, orientação de procedimentos e orientação de objetos, sobreposição e sobrecarga, algoritmo de substituição de página, pr
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

3 de abril de 2024

[História de Desenvolvedor SI] 06. Especificações Necessárias para ingressar em uma empresa SI Para ingressar em uma empresa SI comum, não uma das 3 maiores empresas SI, é útil aprender tecnologias como Spring, MyBatis, HTML, CSS e jQuery, e obter a certificação de Processamento de Informações. O TOEIC, outras certificações e atividades extracurric
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

17 de abril de 2024

Desafios no desenvolvimento da API da Korea Investment Securities Este artigo de blogue é para desenvolvedores que desejam criar programas de negociação automatizada usando a API da Korea Investment Securities. Apresenta as dificuldades encontradas durante o desenvolvimento, como abertura de conta, falta de suporte para
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마
(로또 사는 아빠) 살림 하는 엄마

23 de abril de 2024