网易首页 > 网易号 > 正文 申请入驻

Spring Boot 线程池的使用心得,你真会用吗?

0
分享至

>>号外:关注“Java精选”公众号,回复“2021面试题”,领取免费资料! “ Java精选面试题”小程序,3000+ 道面试题在线刷,最新、 最全 Java 面试题!

前言

前两天做项目的时候,想提高一下插入表的性能优化,因为是两张表,先插旧的表,紧接着插新的表,一万多条数据就有点慢了

后面就想到了线程池ThreadPoolExecutor,而用的是Spring Boot项目,可以用Spring提供的对ThreadPoolExecutor封装的线程池ThreadPoolTaskExecutor,直接使用注解启用

使用步骤

先创建一个线程池的配置,让Spring Boot加载,用来定义如何创建一个ThreadPoolTaskExecutor,要使用@Configuration和@EnableAsync这两个注解,表示这是个配置类,并且是线程池的配置类

@Configuration
@EnableAsync
public class ExecutorConfig {

private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;

@Bean(name = "asyncServiceExecutor")
public Executor asyncServiceExecutor() {
logger.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
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;
}
}

@Value是我配置在application.properties,可以参考配置,自由定义

# 异步线程配置
# 配置核心线程数
async.executor.thread.core_pool_size = 5
# 配置最大线程数
async.executor.thread.max_pool_size = 5
# 配置队列大小
async.executor.thread.queue_capacity = 99999
# 配置线程池中的线程的名称前缀
async.executor.thread.name.prefix = async-service-

创建一个Service接口,是异步线程的接口

public interface AsyncService {
* 执行异步任务
* 可以根据需求,自己加参数拟定,我这里就做个测试演示
void executeAsync();

实现类

@Service
public class AsyncServiceImpl implements AsyncService {
private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);

@Override
@Async("asyncServiceExecutor")
public void executeAsync() {
logger.info("start executeAsync");

System.out.println("异步线程要做的事情");
System.out.println("可以在这里执行批量插入等耗时的事情");

logger.info("end executeAsync");
}
}

将Service层的服务异步化,在executeAsync()方法上增加注解@Async("asyncServiceExecutor"),asyncServiceExecutor方法是前面ExecutorConfig.java中的方法名,表明executeAsync方法进入的线程池是asyncServiceExecutor方法创建的。

接下来就是在Controller里或者是哪里通过注解@Autowired注入这个Service

@Autowired
private AsyncService asyncService;

@GetMapping("/async")
public void async(){
asyncService.executeAsync();
}

用postmain或者其他工具来多次测试请求一下

2018-07-16 22:15:47.655 INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:15:47.655 INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
2018-07-16 22:15:47.770 INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:15:47.770 INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
2018-07-16 22:15:47.816 INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:15:47.816 INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
2018-07-16 22:15:48.833 INFO 10516 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:15:48.834 INFO 10516 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
2018-07-16 22:15:48.986 INFO 10516 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:15:48.987 INFO 10516 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

通过以上日志可以发现,[async-service-]是有多个线程的,显然已经在我们配置的线程池中执行了,并且每次请求中,controller的起始和结束日志都是连续打印的,表明每次请求都快速响应了,而耗时的操作都留给线程池中的线程去异步执行。

虽然我们已经用上了线程池,但是还不清楚线程池当时的情况,有多少线程在执行,多少在队列中等待呢?这里我创建了一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来

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;

/**
* @Author: ChenBin
*/
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 Future submit(Callable 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 ListenableFuture submitListenable(Callable task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}

如上所示,showThreadPoolInfo方法中将任务总数、已完成数、活跃线程数,队列大小都打印出来了,然后Override了父类的execute、submit等方法,在里面调用showThreadPoolInfo方法,这样每次有任务被提交到线程池的时候,都会将当前线程池的基本情况打印到日志中;

修改ExecutorConfig.java的asyncServiceExecutor方法,将ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改为ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor()

@Bean(name = "asyncServiceExecutor")
public Executor asyncServiceExecutor() {
logger.info("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;
}

再次启动该工程测试

2018-07-16 22:23:30.951 INFO 14088 --- [nio-8087-exec-2] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [0], completedTaskCount [0], activeCount [0], queueSize [0]
2018-07-16 22:23:30.952 INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:23:30.953 INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
2018-07-16 22:23:31.351 INFO 14088 --- [nio-8087-exec-3] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [1], completedTaskCount [1], activeCount [0], queueSize [0]
2018-07-16 22:23:31.353 INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:23:31.353 INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
2018-07-16 22:23:31.927 INFO 14088 --- [nio-8087-exec-5] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [2], completedTaskCount [2], activeCount [0], queueSize [0]
2018-07-16 22:23:31.929 INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:23:31.930 INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
2018-07-16 22:23:32.496 INFO 14088 --- [nio-8087-exec-7] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [3], completedTaskCount [3], activeCount [0], queueSize [0]
2018-07-16 22:23:32.498 INFO 14088 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
异步线程要做的事情
可以在这里执行批量插入等耗时的事情
2018-07-16 22:23:32.499 INFO 14088 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

注意这一行日志:

2018-07-16 22:23:32.496 INFO 14088 --- [nio-8087-exec-7] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [3], completedTaskCount [3], activeCount [0], queueSize [0]

这说明提交任务到线程池的时候,调用的是submit(Callable task)这个方法,当前已经提交了3个任务,完成了3个,当前有0个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一路了然。

作者:如漩涡 blog.csdn.net/m0_37701381/article/details/81072774

往期精选 点击标题可跳转

从原理到实践彻底搞懂 Java 日志系统,再也不迷茫了!

如何设计 QQ、微信、微博、Github 等第三方账号登陆 ?(附表设计)

【源码解读】JDK1.8 中 ConcurrentHashMap 不支持空键值对源码剖析

为什么要代码重构?如何重构?常见重构技巧,值得收藏!

面试官问:为什么 Java 线程没有 Running 状态?一下被问懵!

SpringBoot + Mybatis + Druid + PageHelper 实现多数据源并分页(附源码)

Intellij IDEA 中的各种调试代码技巧,轻松定位 Bug 问题(涵盖超全面)

MyBatis 真坑!Integer 类型赋值 0 ,当 != '' 时无法通过判断执行 SQL 语句

面试官问:Spring Boot 中实现通用 Auth 认证,有哪几种方式?

Spring 中 IService 有多个实现类,它是如何知道该注入哪个 ServiceImpl 类?

突然慌了!面试官问:线程池中多余的线程是如何回收的?

点个赞,就知道你“在看”!

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
12秒99夺冠!吴艳妮称破13秒很轻松,早上睡过导致“没机会化妆”

12秒99夺冠!吴艳妮称破13秒很轻松,早上睡过导致“没机会化妆”

里芃芃体育
2026-06-28 08:44:25
3-0周启豪!王楚钦打疯了,最后一球把裁判逗笑!侯英超说出真因!孙颖莎明日双赛

3-0周启豪!王楚钦打疯了,最后一球把裁判逗笑!侯英超说出真因!孙颖莎明日双赛

好乒乓
2026-06-29 11:42:12
尴尬了!广东一家长称收到南科大录取意向,后悔中山大学强基入围

尴尬了!广东一家长称收到南科大录取意向,后悔中山大学强基入围

火山詩话
2026-06-27 16:31:45
法国40℃高温禁空调,民众自嘲不如中国猪

法国40℃高温禁空调,民众自嘲不如中国猪

这样子啊
2026-06-28 05:26:37
3架全球鹰前推2500公里,距中国仅一步之遥,部署日本的4点考量

3架全球鹰前推2500公里,距中国仅一步之遥,部署日本的4点考量

止戈军是我
2026-06-29 13:03:18
快入伏了,家中常备4种零食,每天吃点,气血补充不虚脱,还解馋

快入伏了,家中常备4种零食,每天吃点,气血补充不虚脱,还解馋

花小厨
2026-06-29 10:53:50
62分惨败!男篮世青赛最惨一败:日本66比128惨遭美国队血洗?

62分惨败!男篮世青赛最惨一败:日本66比128惨遭美国队血洗?

篮球快餐车
2026-06-29 01:20:12
胡小伟藏身伦敦豪宅,手握四本护照却从不露面,他到底在怕什么

胡小伟藏身伦敦豪宅,手握四本护照却从不露面,他到底在怕什么

莫地方
2026-06-26 00:55:06
王濛拒领奖离场,怒指节目组不满而非针对姐姐们

王濛拒领奖离场,怒指节目组不满而非针对姐姐们

可爱小菜
2026-06-29 06:56:38
遇“建”·更美好:建发房产以“三大平台+六大业务”锚定城市运营新赛道

遇“建”·更美好:建发房产以“三大平台+六大业务”锚定城市运营新赛道

新京报
2026-06-23 10:03:48
极端高温拷问民生,“政治正确”被批双标,欧洲多国激辩“装不装空调”

极端高温拷问民生,“政治正确”被批双标,欧洲多国激辩“装不装空调”

环球网资讯
2026-06-29 07:12:13
安徽水利厅对马鞍山、芜湖、宣城、铜陵、池州、安庆、黄山市启动洪水防御Ⅳ级应急响应

安徽水利厅对马鞍山、芜湖、宣城、铜陵、池州、安庆、黄山市启动洪水防御Ⅳ级应急响应

界面新闻
2026-06-29 14:32:30
世界杯:阿根廷的轮换与斯卡的带队

世界杯:阿根廷的轮换与斯卡的带队

靴室笑谈社
2026-06-28 14:54:42
俄乌局势逆转,俄罗斯犯下严重战略错误!

俄乌局势逆转,俄罗斯犯下严重战略错误!

一个坏土豆
2026-06-28 19:14:03
官方公开发声回应!上海数十名球迷穿日本球衣呐喊:要多支持邻居

官方公开发声回应!上海数十名球迷穿日本球衣呐喊:要多支持邻居

风过乡
2026-06-29 06:51:48
最高法重击绿卡:绿卡入境恐被当场没收、直接拘留

最高法重击绿卡:绿卡入境恐被当场没收、直接拘留

纽约时间
2026-06-29 04:55:30
太突然!阿根廷球员刚晒一家四口合影,几小时后妻子和孩子全遇难

太突然!阿根廷球员刚晒一家四口合影,几小时后妻子和孩子全遇难

阿废冷眼观察所
2026-06-29 13:15:16
沪指涨逾1% 芯片股午后走强

沪指涨逾1% 芯片股午后走强

每日经济新闻
2026-06-29 14:01:09
1天4个瓜!当街亲密、全网封禁,自曝怀双胎,赵丽颖最让人意外

1天4个瓜!当街亲密、全网封禁,自曝怀双胎,赵丽颖最让人意外

丁丁鲤史纪
2026-06-28 15:35:14
最重伤情!中场大将十字韧带断裂或休战一年 英超豪门将获赔578万

最重伤情!中场大将十字韧带断裂或休战一年 英超豪门将获赔578万

狍子歪解体坛
2026-06-28 23:30:42
2026-06-29 14:56:49
Java精选
Java精选
一场永远也演不完的戏
1795文章数 3859关注度
往期回顾 全部

科技要闻

OpenAI推迟上市,那“Kimi们”呢?

头条要闻

于北辰"210%拦截率"成大陆学校考题 台网红笑称恭喜

头条要闻

于北辰"210%拦截率"成大陆学校考题 台网红笑称恭喜

体育要闻

两周飞5万公里!因凡蒂诺遭环保人士猛批

娱乐要闻

萧蔷宣布捐出参加“浪姐”所有收入

财经要闻

35岁职场人,又好找工作了?

汽车要闻

全新宝马iX3长轴版将于成都车展预售 四季度交付

态度原创

游戏
艺术
教育
家居
房产

MSI:T1强势横扫KC,晋级下一轮!是谁让T1打入围赛的?

艺术要闻

《顽固者的城——胡吉宏的艺术实践》学术交流展于贵州启幕

教育要闻

真是没想到,小学二年级的题目都这么难了

家居要闻

绿意盎然 自然之境

房产要闻

你敢想?海口房地产投资,暴跌5成!

无障碍浏览 进入关怀版