一、前言
异步注解@Async是实现我们程序提高效率的很重要的注解。在方法上添加@Async注解,表示此方法是异步方法, 在类上添加@Async表示类中所有方法都是异步方法,使用此注解的类,必须是Spring管理的类,需要在启动类或配置中加入@EnableAsync注解,@Async才会生效。
在使用Async时,如果不指定线程池的名称,也就是不是自定义线程池,@Async是有默认线程池的,使用的是Spring默认的线程池SimpleAsyncTaskExecutor。
默认线程池的默认配置如下:
00001. 默认核心线程数:8;
00002. 最大线程数:Integet.MAX_VALUE;
00003. 队列使用LinkedBlockingQueue;
00004. 容量是:Integet.MAX_VALUE;
00005. 空闲线程保留时间:60s;
线程池拒绝策略:AbortPolicy
二、基本使用
1.添加依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
2.自定义线程池设置
package com.example.spepcdemo.config;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
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.*;
/**
* @author qx
* @date 2024/6/3
* @des 自定义线程池设置
*/
@EnableAsync
@Configuration
public class AsyncTaskConfig {
@Bean("my-executor")
public Executor executor() {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("my-executor").build();
//获取CPU的处理器数量
int curSystemThreads = Runtime.getRuntime().availableProcessors() * 2;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(curSystemThreads, 100, 200, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), threadFactory);
threadPoolExecutor.allowsCoreThreadTimeOut();
return threadPoolExecutor;
}
@Bean("async-executor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//核心线程数
taskExecutor.setCorePoolSize(10);
//线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
taskExecutor.setMaxPoolSize(100);
//缓存队列
taskExecutor.setQueueCapacity(50);
//空闲时间,当超过了核心线程数之外的线程在空闲时间到达之后会被销毁
taskExecutor.setKeepAliveSeconds(200);
//异步方法内部线程名称
taskExecutor.setThreadNamePrefix("async-executor-");
/**
* 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略
* 通常有以下四种策略:
* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
* ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功
*/
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.initialize();
return taskExecutor;
}
}
3.异步服务类
package com.example.spepcdemo.service;
import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @author qx
* @date 2024/5/28
* @des
*/
@Slf4j
@Service
public class AsyncService {
/**
* 异步任务1
*/
@Async("async-executor")
public void async1() {
ThreadUtil.execAsync(() -> {
log.info("异步执行操作1");
ThreadUtil.sleep(2000);
});
}
/**
* 异步任务2
*/
@Async("async-executor")
public void async2() {
ThreadUtil.execAsync(() -> {
log.info("异步执行操作2");
ThreadUtil.sleep(3000);
});
}
}
4.创建测试类
package com.example.spepcdemo.controller;
import com.example.spepcdemo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author qx
* @date 2024/5/28
* @des 异步操作
*/
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/async")
public void testAsync() {
long start = System.currentTimeMillis();
//调用两个异步方法
asyncService.async1();
asyncService.async2();
System.out.println("完成操作,耗时:" + (System.currentTimeMillis() - start));
}
}
5.测试
我们启动程序在浏览器上访问http://localhost:8080/async。
6.主要事项
@Async失效有几个原因:
1. 注解@Async的方法不是public方法;
2. 注解@Async的返回值只能为void或Future;
3. 注解@Async方法使用static修饰也会失效;
4. 没加@EnableAsync注解;
5. 调用方和@Async不能在一个类中;
6. 在Async方法上标注@Transactional是没用的,但在Async方法调用的方法上标注@Transcational是有效的;