网站首页 > java教程 正文
前言
周日在家午休,刚醒,突然接到同事电话,说线上有个kafka消费者服务崩了,一直在重启,你去看看。一顿排查后,发现进程在启动后的一分钟内必定会占用大量内存,而且进程内的线程瞬间1万+。最后定位到某个方法是用了@Async注解在使用时未指定自定义的线程池,而且kafka。那它是何方神圣
@Async介绍
在Spring中,使用@Async直接的方法,称之为异步方法;这些方法执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,可继续其他的操作。
Spring默认配置
在spring boot项目中我们只需要添加@EnableAsync来开启异步调用,@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor。我们也可以自己定义线程池(推荐)。下面是spring源码中默认的参数
从源码我们可以看出线程池的部分配置:
- 默认核心线程数:1
- 最大线程数:2147483647
- 队列容量是:2147483647
- 空闲线程保留时间:60s
奇怪,没有看到队列使用的是什么以及线程池拒绝策略,于是继续看源码,发现在queueCapacity大于0的时候用的是LinkedBlockingQueue队列,否者用的SynchronousQueue
那线程池拒绝策略呢?查看源码这个地方使用了策略模式,默认是AbortPolicy。
spring默认给的这些参数在并发不高的场景下其实问题不大。但是倘若并发上来这个这个默认的线程池隐患就体现出来了。为什么呢?风险点体现在最大线程数以及默认队列LinkedBlockingQueue的大小上面。
正常情况下,我们生产环境的会设置初始堆-Xms,最大堆的-Xmx等jvm参数,当接口并发远远大于自定义线程池的任务处理速度,那线程池的队列中会堆积很多任务,直接表现是JVM内存突然飙升。这种情况下。如果你直接在使用Spring默认的线程池,那这个LinkedBlockingQueue队列的默认容量就是你生产环境OOM的引爆点。哈哈
如何正确使用@Async
1、使用@EnableAsync注解开启
@EnableAsync
@SpringBootApplication
public class ApiClientApplication {
public static void main(String[] args) {
SpringApplication.run(ApiClientApplication.class, args);
}
}
2.自定义线程池参数
//@EnableAsync 也可以放此处
@Configuration
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
//线程池名的前缀
executor.setThreadNamePrefix("taskExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
3.使用
@Async("taskExecutor")
public void methodA(){
}
使用
1.我们使用单元测试测试一下,首先定义一个任务类
@Slf4j
@Component
public class Task {
@Async("taskExecutor")
public void doTask(int i) throws InterruptedException {
log.info("开始做任务:{}",i);
Random random = new Random();
Thread.sleep(random.nextInt(10000));
log.info("任务结束:{}",i);
}
}
2.单元测试类
@SpringBootTest
@Slf4j
class OrderServiceApiClientApplicationTests {
@Autowired
private Task task;
@Test
public void test() throws Exception {
for (int i = 0; i < 50; i++) {
task.doTask(i);
}
}
3.测试结果
原创不易,点个关注呗
- 上一篇: RabbitMQ与Java集成的典型用例:从消息传递到任务调度的全面探索
- 下一篇:已经是最后一篇了
猜你喜欢
- 2025-05-22 RabbitMQ与Java集成的典型用例:从消息传递到任务调度的全面探索
- 2025-05-22 JAVA面试|Redis原理及应用场景
- 2025-05-22 并发编程:CompletableFuture异步编程没有那么难
- 2025-05-22 06.整合rabbitmq异步处理
- 2025-05-22 同步 vs 异步性能差100倍!SpringBoot3 高吞吐接口实现终极方案
- 2025-05-22 Java高并发处理的艺术:让程序飞起来!
- 2025-05-22 HttpClient的异步调用,你造吗?
- 2025-05-22 @Async:一个异步方法调用另一个异步方法难道不是异步吗?
- 2025-05-22 Serverless革命:Java函数计算性能突破
- 2025-05-22 使用Quarkus开发响应式REST API,异步异步异步
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)