线程池的使用场景
在赛博朋克2077年的数据中心,线程池是连接人类与AI的神经接口——每根光纤(线程)都承载着数据洪流,而调度器就是那个冷静的中央AI,它必须决定:是让所有光纤同时全速运转(资源耗尽),还是像交响乐指挥家一样,让每个音符(任务)在正确的时间响起?
就像你家的快递柜,线程池就是那个能同时存放20个包裹的智能柜。当快递员(任务)不断投递时,你是选择让柜子爆满(线程阻塞),还是让取件人(CPU)分批取件(任务队列)?线程池的妙处,就在于它让'等待'变得优雅,而不是让人抓狂
线程池作为并发编程的核心组件,其应用场景广泛且具有显著性能优势,以下是典型使用场景及技术实现要点:
一、高并发任务处理
在Web服务、API网关等场景中,线程池可限制并发线程数,避免系统资源耗尽。例如处理HTTP请求时,通过固定核心线程数(如CPU核数×2)和合理队列容量(如LinkedBlockingQueue)实现请求缓冲1。
二、批量任务执行
- 数据导入/导出:如Elasticsearch批量导入场景,通过线程池并行处理数据分片,显著提升吞吐量。需注意批量大小调优和ES集群负载监控2。
- 多数据源聚合:从微服务或数据库并行采集数据后汇总,利用CountDownLatch同步结果。
三、异步非阻塞操作
- IO密集型任务:邮件发送、日志记录、第三方API调用等场景,通过提交任务到线程池避免主线程阻塞。
- GUI应用:后台任务(如文件下载)与界面渲染分离,提升用户体验。
四、定时与周期性任务
通过ScheduledThreadPool实现定时任务调度,如定时数据备份、心跳检测等。
五、资源受限系统
在嵌入式或内存敏感环境中,线程池可限制线程数量,减少内存和CPU消耗,确保系统稳定性。
六、AI与高性能计算
- 并行推理:如OpenALPR通过线程池实现车牌识别的多核并行处理,任务队列采用生产者-消费者模型。
- 任务调度优化:AI训练中结合GPU计算与线程池流水线设计,提升资源利用率。
七、特殊场景优化
- Tomcat模式改造:Spring线程池可调整为核心线程→非核心线程→队列的优先级策略,适应高并发需求3。
- TraceID传递:通过封装ThreadPoolExecutor或使用Spring的ThreadPoolTaskExecutor,确保日志链路追踪8。
线程池配置需结合任务类型(CPU/IO密集型)和硬件资源,通过压测确定最优参数49。
搭建(粘贴即用)
如果把程序比作一家公司,线程池就是那个永远在加班的HR部门——明明招聘需求已经堆成山(任务队列),却总被老板(CPU)要求'再优化一下资源利用率'。而作为程序员,我们既要让这个HR部门高效运转,又要避免它因为过劳罢工(线程耗尽),这中间的平衡艺术,就是今天要聊的线程池生存指南。
1.1 线程池配置中心
线程池的核心参数共有七个,它们共同决定了线程池的行为特性和资源管理方式。
核心线程数(corePoolSize) 定义了线程池中长期维持的线程数量,即使这些线程处于空闲状态,通常也不会被回收,从而保证了线程池的基本处理能力。
最大线程数(maximumPoolSize) 是线程池允许创建的最大线程数量上限,当任务量激增且工作队列已满时,线程池可以创建新线程直至达到此值。
线程存活时间(keepAliveTime) 和 时间单位(unit) 共同规定了非核心线程空闲时的最长存活时间,超时后这些线程将被终止以回收系统资源。
工作队列(workQueue) 作为任务的缓冲区域,用于保存待执行的任务,其类型(如ArrayBlockingQueue、LinkedBlockingQueue)直接影响线程池的排队机制和任务调度策略。
线程工厂(threadFactory) 用于自定义线程的创建方式,开发者可以通过它来设置线程名称、优先级等属性。
拒绝策略(rejectedExecutionHandler) 在线程池和队列均已满时被触发,用于处理无法接纳的新任务,常见的策略有直接抛出异常(AbortPolicy)或由调用者线程直接执行任务(CallerRunsPolicy)45。
这些参数需要根据具体的应用场景进行合理配置,例如对于计算密集型任务,通常建议将核心线程数设置为CPU核心数加一3。
@EnableAsync
@Configuration
public class ThreadPoolConfiguration {
@Primary
@Bean(ThreadPoolConsts.THREAD_POOL_NAME_DEFAULT)
public Executor taskExecutor() {
return buildExecutorByConfig(ThreadPoolConsts.THREAD_POOL_NAME_DEFAULT, null, null);
}
private Executor buildExecutorByConfig(String configKey, Integer corePoolSize, Integer maxPoolSize) {
ThreadPoolProperties config = new ThreadPoolProperties();
ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
executor.setCorePoolSize(config.getCorePoolSize());
executor.setMaxPoolSize(config.getMaxPoolSize());
executor.setQueueCapacity(config.getQueueCapacity());
executor.setThreadNamePrefix(configKey + "-");
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
executor.setWaitForTasksToCompleteOnShutdown(config.isWaitForTasksToCompleteOnShutdown());
executor.setAwaitTerminationSeconds(config.getAwaitTerminationSeconds());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
if (null != corePoolSize) {
executor.setCorePoolSize(maxPoolSize);
}
if (null != corePoolSize) {
executor.setMaxPoolSize(maxPoolSize);
}
executor.initialize();
return executor;
}
}
在程序的江湖里,线程池便是那'少林罗汉堂'——看似静默,实则暗藏三百六十五路精兵。当江湖突现紧急任务(高并发请求),方丈(调度器)轻敲木鱼,便有一队罗汉(线程)应声而出。但若任务如潮水般涌来,罗汉堂如何避免'人海战术'的消耗?且听我分解这'以少胜多'的线程池兵法
1.2 线程池名称
public class ThreadPoolConsts {
private ThreadPoolConsts() {
}
/**
* 公共线程池
*/
public static final String THREAD_POOL_NAME_DEFAULT = "commonExecutor";
}
想象你是一位米其林主厨,线程池就是你的厨房团队——厨师(线程)们各司其职,切菜(任务处理)的、炒菜(计算)的、摆盘(IO等待)的。但突然涌入十桌订单(高并发),你是让所有厨师同时切十份洋葱(资源浪费),还是让一位厨师先切完再传菜(任务队列)?线程池的智慧,就在这'厨房调度'的学问里。
1.3 配置明细
@Component
@ConfigurationProperties(prefix = "xxx.thread-pool", ignoreUnknownFields = true)
public class ThreadPoolProperties {
private int corePoolSize = 10;
private int maxPoolSize = 20;
private int queueCapacity = 500;
private int keepAliveSeconds = 60;
private boolean waitForTasksToCompleteOnShutdown = true;
private int awaitTerminationSeconds = 60;
public int getCorePoolSize() {
return corePoolSize;
}
public void setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getQueueCapacity() {
return queueCapacity;
}
public void setQueueCapacity(int queueCapacity) {
this.queueCapacity = queueCapacity;
}
public int getKeepAliveSeconds() {
return keepAliveSeconds;
}
public void setKeepAliveSeconds(int keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
}
public boolean isWaitForTasksToCompleteOnShutdown() {
return waitForTasksToCompleteOnShutdown;
}
public void setWaitForTasksToCompleteOnShutdown(boolean waitForTasksToCompleteOnShutdown) {
this.waitForTasksToCompleteOnShutdown = waitForTasksToCompleteOnShutdown;
}
public int getAwaitTerminationSeconds() {
return awaitTerminationSeconds;
}
public void setAwaitTerminationSeconds(int awaitTerminationSeconds) {
this.awaitTerminationSeconds = awaitTerminationSeconds;
}
}
1.4 可视化线程池任务执行器
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);
@Override
public void execute(Runnable task) {
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
showThreadPoolInfo();
super.execute(new TaskWrapper(task, copyOfContextMap));
}
@Override
public void execute(Runnable task, long startTimeout) {
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
showThreadPoolInfo();
super.execute(new TaskWrapper(task, copyOfContextMap), startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
showThreadPoolInfo();
return super.submit(new TaskWrapper(task, copyOfContextMap));
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
showThreadPoolInfo();
return super.submitListenable(new TaskWrapper(task, copyOfContextMap));
}
@Override
public <T> Future<T> submit(Callable<T> task) {
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
showThreadPoolInfo();
return super.submit(new FutureTaskWrapper<>(task, copyOfContextMap));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
showThreadPoolInfo();
return super.submitListenable(new FutureTaskWrapper<>(task, copyOfContextMap));
}
/**
* 日志打印
*/
private void showThreadPoolInfo() {
ThreadPoolExecutor threadPoolExecutor = this.getThreadPoolExecutor();
LOGGER.info("ThreadPoolInfo, name: {}, activeCount: {}, taskCount: {}, completedTaskCount: {}",
this.getThreadNamePrefix(), threadPoolExecutor.getActiveCount(), threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount());
}
public static class TaskWrapper implements Runnable {
private final Runnable gift;
private final Map<String, String> mdcContext;
public TaskWrapper(Runnable target, Map<String, String> mdcContext) {
this.gift = target;
this.mdcContext = mdcContext;
}
@Override
public void run() {
if (this.gift == null) {
return;
}
if (this.mdcContext != null) {
MDC.setContextMap(mdcContext);
}
try {
this.gift.run();
} catch (Exception e) {
LOGGER.error("TaskWrapper target execute exception.", e);
} finally {
MDC.clear();
}
}
}
public static class FutureTaskWrapper<T> implements Callable<T> {
private final Callable<T> gift;
private final Map<String, String> mdcContext;
public FutureTaskWrapper(Callable<T> target, Map<String, String> mdcContext) {
this.gift = target;
this.mdcContext = mdcContext;
}
@Override
public T call() throws Exception {
if (this.gift == null) {
return null;
}
if (this.mdcContext != null) {
MDC.setContextMap(mdcContext);
}
try {
return this.gift.call();
} catch (Exception e) {
LOGGER.error("FutureTaskWrapper target execute exception.", e);
} finally {
MDC.clear();
}
return null;
}
}
}
使用(一眼懂)
2.1 做异步或者离线任务 ...
@Resource(name = "commonExecutor")
private Executor executor;
private void useExecutor() {
executor.execute(() -> {
doYourWork();
});
}
2.2 配合 CountDownLatch 使用
@Resource(name = "commonExecutor")
private Executor commonExecutor;
CountDownLatch cdl = new CountDownLatch(taskList.size());
taskList.forEach(task -> commonExecutor.execute(() -> {
try {
doSomeThing(task)
} catch (Exception e) {
LOGGER.error("写的破代码抛出来一个异常:", e);
} finally {
cdl.countDown();
}
}));
try {
cdl.await();
} catch (InterruptedException e) {
LOGGER.error("写的破代码抛出来一个异常:", e);
Thread.currentThread().interrupt();
}
2.3 配合 CompletableFuture 使用
@Resource(name = "commonExecutor")
private Executor commonExecutor;
private List<XXXResp> syncGetAllResult(List<XXXReq> reqList) {
List<XXXResp> resultList = Lists.newArrayList();
// 加入线程池异步处理
List<CompletableFuture<Map<String, List<XXXResp>>>> futureList = reqList
.stream().map(req -> CompletableFuture
.supplyAsync(() -> getCurrResult(req), commonExecutor)
.exceptionally(ex -> {
throw new ServiceException(String.format("初始化数据失败入参(%s),异常: %s", JSONUtil.toJsonStr(req), ex.getMessage()));
})
).collect(Collectors.toList());
//获取所有执行结果
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
//将所有结果汇总
CompletableFuture<List<Map<String, List<XXXResp>>>> allContentsFuture = allFutures.thenApply(v ->
futureList.stream().map(CompletableFuture::join).collect(Collectors.toList()));
try {
List<Map<String, List<XXXResp>>> list = allContentsFuture.get();
list.forEach(m -> m.forEach((k, respList) -> resultList.addAll(respList)));
} catch (Exception e) {
LOGGER.error("获取结果异常:{}", e.getMessage());
Thread.currentThread().interrupt();
}
return resultList;
}
工具类封装(并行任务)
public enum CompletableFutureSimpleThreadPool {
/**
* 单例对象
*/
INSTANCE;
@Resource(name = "commonExecutor")
private Executor executor;
private static final Logger log = LoggerFactory.getLogger(CompletableFutureSimpleThreadPool.class);
/**
* 执行多个任务(无返回值)
*
* @param tasks 任务数组
*/
public static void executeTasks(@Nonnull Runnable... tasks) {
if (ObjectUtil.isNull(tasks)) {
log.error("[线程池][执行任务] 任务为空");
return;
}
// 使用线程池并行执行多个任务
CompletableFuture<?>[] futures = Arrays.stream(tasks)
.map(task -> CompletableFuture.supplyAsync(() -> task, INSTANCE.executor)
.exceptionally(ex -> {
throw new BusinessException(String.format("执行任务异常: %s", ex));
})).toArray(CompletableFuture[]::new);
// 等待所有任务执行完成
CompletableFuture.allOf(futures).join();
}
/**
* 执行多个任务(有返回值,使用 CompletableFuture 作为任务)
*
* @param tasks 任务数组
* @return 任务执行结果的列表
*/
@SafeVarargs
public static <T> List<T> executeCompletableFutures(@Nonnull CompletableFuture<T>... tasks) {
if (ObjectUtil.isNull(tasks)) {
log.error("[线程池][执行任务] 任务为空");
return Collections.emptyList();
}
// 使用 allOf 等待所有任务完成
CompletableFuture<Void> allOf = CompletableFuture.allOf(tasks);
allOf.join();
// 收集任务结果
return Arrays.stream(tasks)
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
/**
* 执行多个任务(有返回值,使用 Supplier 作为任务)
*
* @param tasks 任务数组
* @return 任务执行结果的列表
*/
@SafeVarargs
public static <T> List<T> executeSuppliers(@Nonnull Supplier<T>... tasks) {
if (ObjectUtil.isNull(tasks)) {
log.error("[线程池][执行任务] 任务为空");
return Collections.emptyList();
}
// 执行多个任务并收集结果
List<CompletableFuture<T>> futures = Arrays.stream(tasks)
.map(task -> CompletableFuture.supplyAsync(() -> {
try {
return task.get();
} catch (Exception e) {
log.error("任务执行失败", e);
return null; // 或者可以选择抛出 RuntimeException
}
}, INSTANCE.executor))
.collect(Collectors.toList());
// 获取所有任务的执行结果
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}
所以,下次当你的程序因为线程池配置不当而卡成PPT时,不妨想想:这就像让一个HR部门同时处理100份简历,结果所有人都在吵架谁先看谁的。记住,线程池不是许愿池——扔多少任务都不会自动变快,但合理配置的线程池,绝对能让你的程序从'996ICU'变成'准点下班'的模范员工。
最后,送各位程序员一句厨房箴言:线程池不是让你把所有食材(任务)同时扔进锅(CPU),而是像米其林主厨一样,让每个步骤(线程)在正确的时间完成。毕竟,谁也不想吃一道'夹生饭'(任务未完成)的程序,对吧?