Tutorial básico do springboot2.x: @Async abre tarefas assíncronas

Em projetos de desenvolvimento, geralmente temos cenários em que precisamos iniciar tarefas assíncronas. Por exemplo, quando o usuário se registra com sucesso, alguns cupons precisam ser emitidos. Neste momento, para evitar que essas operações adicionais afetem o processo de registro do usuário, geralmente iniciamos um thread de forma assíncrona para executar a lógica de distribuição de cupons.
Normalmente, precisamos definir um pool de threads e iniciar uma tarefa de thread. Ele é simplificado no Springboot e um pool de threads de tarefas do tipo org.springframework.core.task.TaskExecutor é configurado automaticamente. Quando ativamos a anotação @EnableAsync, quando precisamos realizar tarefas assíncronas, adicione anotações @Async, este método Irá iniciar automaticamente um thread para ser executado.
Ao explicar como configurar o Springboot para iniciar tarefas assíncronas, vamos explicar brevemente o conhecimento básico do pool de threads Java.

Conhecimento básico de pool de threads

Use cenários de pool de threads

Normalmente alta simultaneidade, o tempo de execução da tarefa é curto, o tempo de criação do thread é T1, o tempo de execução da tarefa é T2 e o tempo de destruição do thread é T3. Quando o tempo de T1 + T2 é muito maior do que T2, o pool de threads pode ser usado. A função principal é reutilizar threads e reduzir a sobrecarga de tempo de criação e destruição de threads.
Conte convenientemente as informações de execução da tarefa, como o número de tarefas concluídas.

A interface de nível superior do pool de threads do Executor

Após a versão Jdk1.5, o pool de threads é fornecido para os desenvolvedores criarem facilmente suas próprias tarefas multi-threaded. A interface java.util.concurrent.Executor é a interface de nível superior do pool de encadeamentos.
A interface execute recebe uma instância de thread Runnable para executar uma tarefa.

public interface Executor {
    void execute(Runnable command);
}

Explicação detalhada da interface ExecutorService

public interface ExecutorService extends Executor {

    /**关闭线程池不会接收新的任务,内部正在跑的任务和队列里等待的任务,会执行完
     */
    void shutdown();

    /**
     * 关闭线程池,不会接收新的任务
	 * 尝试将正在跑的任务interrupt中断
	 * 忽略队列里等待的任务
	 * 返回未执行的任务列表
     */
    List<Runnable> shutdownNow();

    /**
     * 返回线程池是否被关闭
     */
    boolean isShutdown();

    /**
     *线程池的任务线程是否被中断
     */
    boolean isTerminated();

    /**
	*接收timeout和TimeUnit两个参数,用于设定超时时间及单位。
	* 当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。和shutdown方法组合使用。
     */
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
	 * 提交任务会获取线程的执行结果
     */
    <T> Future<T> submit(Callable<T> task);

    /**
	* 提交任务会获取线程的T类型的执行结果
     */
    <T> Future<T> submit(Runnable task, T result);

    /**
     * 提交任务
     */
    Future<?> submit(Runnable task);

    /**
     * 批量提交返回任务执行结果
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;

    /**
     * 批量提交返回任务执行结果
	 *增加了超时时间控制,这里的超时时间是针对的所有tasks,而不是单个task的超时时间。
	 *如果超时,会取消没有执行完的所有任务,并抛出超时异常
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * 取得第一个方法的返回值,当第一个任务结束后,会调用interrupt方法中断其它任务
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    /**
     * 取得第一个方法的返回值,当第一个任务结束后,会调用interrupt方法中断其它任务
	 * 任务执行超时抛出异常
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Análise de parâmetro ThreadPoolExecutor

ThreadPoolExecutor é uma interface usada para processar tarefas assíncronas. Pode ser entendido como um pool de threads e uma fila de tarefas. As tarefas enviadas para o objeto ExecutorService serão colocadas na equipe de tarefas ou executadas diretamente por threads no pool de threads. ThreadPoolExecutor suporta ajustes Construa parâmetros para configurar diferentes estratégias de processamento de tarefas.
O código do construtor do pool de threads ThreadPoolExecutor é postado abaixo:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

O significado de cada parâmetro de entrada de ThreadPoolExecutor: A relação entre os parâmetros:

  1. Obtenha threads disponíveis do pool de threads para realizar tarefas. Se não houver threads disponíveis, use ThreadFactory para criar novas threads até que o número de threads atinja o limite corePoolSize
  2. Depois que o número de threads no pool de threads atingir corePoolSize, novas tarefas serão colocadas na fila até que a fila não possa mais acomodar mais tarefas
  3. Quando a fila não puder mais conter mais tarefas, um novo thread será criado até que o número de threads atinja o limite maxinumPoolSize
  4. Depois que o número de threads atingir o limite maxinumPoolSize, a nova tarefa será rejeitada para execução e o RejectedExecutionHandler será chamado para processamento

Estratégia de rejeição RejectedExecutionHandler

1. AbortPolicy: lança uma exceção diretamente, a política padrão;
2. CallerRunsPolicy: usa o thread do chamador para executar a tarefa;
3. DiscardOldestPolicy: descarta a primeira tarefa na fila de bloqueio e executa a tarefa atual;
4. DiscardPolicy: descarta diretamente tarefa;

Pontos-chave do uso de ThreadPoolExecutor

  1. Configure razoavelmente o tamanho do pool de threads: Se for uma tarefa com uso intensivo de CPU, o número de pools de threads pode ser definido como o número de núcleos de CPU + 1, se for uma tarefa intensiva de E / S, a CPU ficará mais ociosa e o número de pools de threads deve ser definido como o número de núcleos de CPU * 2
  2. Fila de tarefas: A fila de tarefas geralmente usa LinkedBlockingQueue para especificar o tamanho e é usada em conjunto com a estratégia de rejeição. O padrão é Integer.Max_VALUE, que está sujeito a estouro de memória.
  3. Exceção de tarefa: adicione try {} catch {} para capturar a exceção na atenção da tarefa no pool de threads, que é conveniente para solução de problemas

Conjunto de threads SpringBoot

org.springframework.core.task.TaskExecutor é a classe de interface do pool de thread assíncrono do Spring e sua essência é java.util.concurrent.Executor. O pool de threads usado por springboot2.3.x por padrão é ThreadPoolTaskExecutor, que pode ser facilmente ajustado por TaskExecutionProperties



SpringBoot define seu próprio pool de thread assíncrono

@EnableAsync
@Component
@Slf4j
public class AsyncConfig implements AsyncConfigurer {
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(8);
        // 设置最大线程数
        executor.setMaxPoolSize(16);
        // 设置队列容量
        executor.setQueueCapacity(50);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        //设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
        executor.setAwaitTerminationSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("CodehomeAsyncTask-");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.initialize();
        return executor;
    }
    public Executor getAsyncExecutor() {
        return taskExecutor();
    }
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new MyAsyncExceptionHandler();
    }

    /**
     * 自定义异常处理类
     */
    class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
            log.info("Exception message - " + throwable.getMessage());
            log.info("Method name - " + method.getName());
            for (Object param : objects) {
                log.info("Parameter value - " + param);
            }
        }
    }
}

Duas maneiras de usar


@Component
@Slf4j
public class UserServiceSyncTask {
	//不带返回值
    @Async
    public void sendEmail(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info(Thread.currentThread().getName());
    }
	//带返回值
    @Async
    public Future<String> echo(String msg){
        try {
            Thread.sleep(5000);
            return new AsyncResult<String>(Thread.currentThread().getName()+"hello world !!!!");
        } catch (InterruptedException e) {
            //
        }
        return null;
    }
}

Mil milhas começam com um único passo. Aqui está o décimo artigo da série de tutoriais do SpringBoot. Todos os códigos-fonte do projeto podem ser baixados no meu GitHub .

Acho que você gosta

Origin blog.csdn.net/github_35592621/article/details/108248916
Recomendado
Clasificación