@Async注解实现异步调用

Posted 赵晓东-Nastu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了@Async注解实现异步调用相关的知识,希望对你有一定的参考价值。

前言

先说一下我们为什么会使用这个注解,当我们在执行逻辑的时候,有一个不是很关键的业务,就是保存日志,因为保存日志它不是客户需求,又为了减小系统与用户之间的延迟,那我们就可以重新启动一个线程去执行保存日志,主线程继续执行业务即可。

同步

同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。

异步

异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法;如他们都是同步调用,则需要将他们都顺序执行完毕之后,方算作过程执行完毕; 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的业务子线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。

配置

业务

/**
 * @Classname AsyncService
 * @Description 异步线程
 * @Date 2022/2/23 16:32
 * @Author zhaoxiaodong
 */
@Slf4j
@Async
@Component
public class AsyncService 

    public void test01()
        Thread thread = Thread.currentThread();
        log.info("线程--->",thread.getName());
    

配置

package com.example.zxdtest.config;

import com.alibaba.ttl.threadpool.TtlExecutors;
import com.example.zxdtest.ZxdtestApplication;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.task.TaskExecutionProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;

/**
 * @Classname AsyncConfig
 * @Description 异步线程池的配置
 * @Date 2022/2/23 16:33
 * @Author zhaoxiaodong
 */
@Slf4j
@EnableAsync
@Configuration
public class AsyncConfig 
    private final TaskExecutionProperties taskExecutionProperties;

    @Autowired
    public AsyncConfig(TaskExecutionProperties taskExecutionProperties)
        this.taskExecutionProperties = taskExecutionProperties;
    

    @Bean()
    public Executor taskExecutor()
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize());
        taskExecutor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize());
        taskExecutor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity());
        taskExecutor.setKeepAliveSeconds((int) taskExecutionProperties.getPool().getKeepAlive().getSeconds());
        taskExecutor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix());
        taskExecutor.setWaitForTasksToCompleteOnShutdown(taskExecutionProperties.getShutdown().isAwaitTermination());
        taskExecutor.setAwaitTerminationSeconds((int) taskExecutionProperties.getShutdown().getAwaitTerminationPeriod().getSeconds());
        taskExecutor.setRejectedExecutionHandler(new Hc360CrmRejectedExecutionHandler());
        taskExecutor.initialize();
        // return taskExecutor;
        return TtlExecutors.getTtlExecutor(taskExecutor);
    
    private static class Hc360CrmRejectedExecutionHandler implements RejectedExecutionHandler 
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) 
            String msg = String.format("Threadpool is exhausted, Reject Task Name:%s, Pool Size:%d (active:%d, core:%d, max:%d, largest:%d), Task:%d (completed:%d), Executor status: (isShutdown:%s, isTerminated:%s, isTerminating:%s)",
                    r.toString(), executor.getPoolSize(), executor.getActiveCount(), executor.getCorePoolSize(), executor.getMaximumPoolSize(), executor.getLargestPoolSize(),
                    executor.getTaskCount(), executor.getCompletedTaskCount(), executor.isShutdown(), executor.isTerminated(), executor.isTerminating());
            log.warn(msg);
            try 
                executor.getQueue().offer(r, 60, TimeUnit.SECONDS);
             catch (InterruptedException ignored) 
            
        
    


YML的配置

spring:
  task:
    execution:
      thread-name-prefix: ZXD-ESPROJECT-ThreadPool-
      pool:
        core-size: 10
        max-size: 50
        queue-capacity: 50
        keep-alive: PT30S
        allow-core-thread-timeout: true
      shutdown:
        await-termination: true
        await-termination-period: PT2M

单元测试


@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
class ZxdtestApplicationTests 

    @Autowired
    private AsyncService asyncService;
    @Autowired
    private ZxdTestService zxdTestService;

    @Test
    void contextLoads() 
       asyncService.selectAsyncDo();
    
    @Test
    void test1()
        zxdTestService.testOne();;
    


```java
2022-02-23 18:24:41.244 - INFO 85300 --- [   HC360-CRM-ThreadPool-1] com.hc360.crm.service.AsyncService       : 线程--->ZXD-ESPROJECT-ThreadPool-

总结

我们用@Async最好要重写线程池, Spring应用默认的线程池,指在@Async注解在使用时,不指定线程池的名称。查看源码,@Async的默认线程池为SimpleAsyncTaskExecutor。每次都会重新创建一个线程。

以上是关于@Async注解实现异步调用的主要内容,如果未能解决你的问题,请参考以下文章

springboot 的异步调用 @Async注解

@Async注解实现异步调用

SpringBoot @Async实现异步调用

Springboot 使用@Async开启异步调用

Spring Boot 微服务异步调用 @EnableAsync @Async

使用@Async 注解异步调用方法,注意事项,解决方案