线程池ThreadPoolTaskExecutor的同步及异步使用
参考信息
本人参考的是这一篇,描述方面比本人好得多: springboot线程池的使用和扩展 VisiableThreadPoolTaskExecutor
背景:
简略记一下,笔记:
- 目标是想在 springboot服务下,自定义一个线程池,然后使用异步,原目的是为了批量导入用。
项目架构
- 普通的springboot服务
步骤
1、先定义一个 ExecutorConfig 类
为了方便,Executor 实现类也可以放在这个config 类。用 @Bean 注解一下也是可以的
并且要加上 @Configuration @EnableAsync 这两个注解~~
import org.springframework.beans.factory.annotation.Value; 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.Executor; import java.util.concurrent.ThreadPoolExecutor; import lombok.extern.slf4j.Slf4j; /** * 线程池config * */ @Configuration @EnableAsync @Slf4j public class ExecutorConfig { // 核心线程数 @Value("${async.executor.thread.core_pool_size:5}") private int corePoolSize; // 最大线程数 @Value("${async.executor.thread.max_pool_size:10}") private int maxPoolSize; // 线程池队列数 @Value("${async.executor.thread.queue_capacity:2000}") private int queueCapacity; // 线程池前缀 @Value("${async.executor.thread.name.prefix:async-task-}") private String namePrefix; @Bean(name = "asyncServiceExecutor") public Executor asyncServiceExecutor() { log.warn("start asyncServiceExecutor"); //在这里修改 ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor(); //配置核心线程数 executor.setCorePoolSize(corePoolSize); //配置最大线程数 executor.setMaxPoolSize(maxPoolSize); //配置队列大小 executor.setQueueCapacity(queueCapacity); //配置线程池中的线程的名称前缀 executor.setThreadNamePrefix(namePrefix); // rejection-policy:当pool已经达到max size的时候,如何处理新任务 // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //执行初始化 executor.initialize(); return executor; } }
2、这里用到了一个 VisiableThreadPoolTaskExecutor 类, 这个就是为了对线程池运行过程中的细节进行记录的一个类。
看下代码就清楚了:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.util.concurrent.ListenableFuture; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; /** * ThreadPoolTaskExecutor的子类 */ public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class); private void showThreadPoolInfo(String prefix){ ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor(); if(null==threadPoolExecutor){ return; } logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]", this.getThreadNamePrefix(), prefix, threadPoolExecutor.getTaskCount(), threadPoolExecutor.getCompletedTaskCount(), threadPoolExecutor.getActiveCount(), threadPoolExecutor.getQueue().size()); } @Override public void execute(Runnable task) { showThreadPoolInfo("1. do execute"); super.execute(task); } @Override public void execute(Runnable task, long startTimeout) { showThreadPoolInfo("2. do execute"); super.execute(task, startTimeout); } @Override public Future<?> submit(Runnable task) { showThreadPoolInfo("1. do submit"); return super.submit(task); } @Override public <T> Future<T> submit(Callable<T> task) { showThreadPoolInfo("2. do submit"); return super.submit(task); } @Override public ListenableFuture<?> submitListenable(Runnable task) { showThreadPoolInfo("1. do submitListenable"); return super.submitListenable(task); } @Override public <T> ListenableFuture<T> submitListenable(Callable<T> task) { showThreadPoolInfo("2. do submitListenable"); return super.submitListenable(task); } }
3、其实到此,准备工作就做完了。。。接下来就是调用了。
controller 层, service 层。。。emmm,这里暂不详述了,参考的博文写得更好。。。
大概示意一下service层的同步异步方法的调用。。:
- 线程池同步调用的方式:
意思就是, 一个请求过来,会等线程池里面执行完成后,再返回。
异步,就不等待了,直接响应回去了~
// 同步用法,要把 Executor 注入进来,并指定 name @Resource(name = "asyncServiceExecutor") private Executor executor; @Override public void showme() { log.info("start>>>>>>"); executor.execute(()->{ // 模拟请求。。 // 从日志上看, start >>>>> 的打印时间和 end >>>>>> 的打印时间,中间是会隔着3秒的 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }); log.info("end>>>>>>"); }
- 异步用法
// 异步用法,就不用这两行了。。。 // @Resource(name = "asyncServiceExecutor") // private Executor executor; // 但是要在方法上面,加上这一行了,重要,重要~ @Async("asyncServiceExecutor") @Override public void showme() { log.info("start>>>>>>"); // 模拟请求。。 // 这里用睡眠3秒表示请求,但实际上这个方法,并不会阻塞,它会马上返回。。。 // 用日志上看到, start >>>>> 和 end >>>> 是在同一时间戳打印的。。。 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } log.info("end>>>>>>"); }
本人比较懒,也只是随手笔记,如有错漏请指正!谢谢。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南