1. @Async的使用
- 在SpringBoot项目中创建application.yml配置文件
# 配置线程池
threadPoolTaskExecutor:
corePoolSize: 10 # 核心线程数(默认线程数)
maxPoolSize: 100 # 最大线程数
keepAliveTime: 10 # 允许线程空闲时间(单位:默认为秒)
queueCapacity: 200 # 缓冲队列数
threadNamePrefix: executor-async- # 线程名统一前缀
server:
port: 8099
spring:
application:
name: asyncServer
配置线程前缀名称原因?
方便溯源,因为并发环境下,出现的内存溢出等问题,非常不好排查,线程有名称方便我们排查问题
- 线程池配置
package com.lby.async.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @description 线程池配置
*/
@Configuration
public class ThreadPoolTaskExecutorConfig {
/**
* 核心线程数(默认线程数)
*/
@Value("${threadPoolTaskExecutor.corePoolSize}")
private int corePoolSize;
/**
* 最大线程数
*/
@Value("${threadPoolTaskExecutor.maxPoolSize}")
private int maxPoolSize;
/**
* 允许线程空闲时间(单位:默认为秒)
*/
@Value("${threadPoolTaskExecutor.keepAliveTime}")
private int keepAliveTime;
/**
* 缓冲队列数
*/
@Value("${threadPoolTaskExecutor.queueCapacity}")
private int queueCapacity;
/**
* 线程池名前缀
*/
@Value("${threadPoolTaskExecutor.threadNamePrefix}")
private String threadNamePrefix;
@Bean("testTaskExecutor")
public ThreadPoolTaskExecutor taskExecutor1() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//设置核心线程数
executor.setCorePoolSize(corePoolSize);
//设置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//线程池所使用的缓冲队列
executor.setQueueCapacity(queueCapacity);
//等待任务在关机时完成--表明等待所有线程执行完
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止
executor.setKeepAliveSeconds(keepAliveTime);
// 线程名称前缀
executor.setThreadNamePrefix(threadNamePrefix);
// 线程池对拒绝任务的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 初始化
executor.initialize();
return executor;
}
}
- 接口和实现类
public interface TestAsyncService {
void testExecute();
void testAsync1();
void testAsync2();
}
@Service
@Slf4j
class TestAsyncServiceImpl implements TestAsyncService {
@Resource(name = "testTaskExecutor")
private ThreadPoolTaskExecutor testTaskExecutor;
@Override
public void testExecute() {
testTaskExecutor.execute(() -> {
log.info("testTaskExecutor.execute当前线程:" + Thread.currentThread().getName());
});
}
// 指定了自定义线程池
@Async("testTaskExecutor")
@Override
public void testAsync1() {
log.info("testAsync1当前线程:" + Thread.currentThread().getName());
}
// 没有指定
@Async
@Override
public void testAsync2() {
log.info("testAsync2当前线程:" + Thread.currentThread().getName());
}
}
- 单元测试
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class AsyncApplicationTests {
@Autowired
private TestAsyncService testAsyncService;
@Test
public void testExecute(){
testAsyncService.testExecute();
}
@Test
public void testAsync1(){
testAsyncService.testAsync1();
}
@Test
public void testAsync2(){
testAsyncService.testAsync2();
}
}
- 启动类
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
2. @Async的使用为什么不建议使用默认的线程池
@Async默认线程池是SimpleAsyncTaskExecutor,里面默认配置线程最大数是Integer.Integer.MAX_VALUE,不对线程做限制,相当于每次使用都创建新线程,当并发量很高时严重影响性能,因此使用自定义的线程池
3. @Async失效的情况
- 启动类没有加@EnableAsync注解
- 方法不是pulibc
因为@Async是通过代理执行的,代理无法拦截非public方法 - @Async方法不是被代理对象执行的,而是被当前类调用执行的
因为@Async是通过代理对象调用执行的;如果我们通过普通(同步方法),调用异步@Async方法的就是当前对象,而不是当前类的代理对象
例如:错误案例
@Component
public class MyService {
@Async("自定义线程池")
public void asyncMethod() {}
// 下面就是就相当于普通方法的普通调用,无法使用异步
public void normalMethod() {
asyncMethod();
}
}
- 方法的返回值不是void或者Future