@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注解实现异步调用的主要内容,如果未能解决你的问题,请参考以下文章