什么情况下用线程池,怎么用?看完就会

先来个最直白的场景

周末你在奶茶店打工,店里只有一台封口机。如果3个顾客排队,你一个人就能应付。突然来了30个顾客,你还是一个人,就会手忙脚乱,所有顾客都得等半天。

程序里也一样:

每个请求都new Thread(),就像每次都去雇一个临时工,干完就开除,上手慢,还贵。 线程池就是提前招好5个固定员工(核心线程),再准备10个兼职(最大线程),忙不过来就让顾客排队(队列)。

一、什么时候该用线程池?

应用场景:

我们公司有个后台系统,每天早上8点要给几百个客户发邮件,以前是这么写的:

java 复制代码
for (Customer c : customers) {
    sendEmail(c.getEmail()); // 一个一个发,串行
}

结果发500封邮件,跑了快20分钟。

后来我改了一下,用了线程池:

java 复制代码
ExecutorService executor = Executors.newFixedThreadPool(10);
for (Customer c : customers) {
    executor.submit(() -> sendEmail(c.getEmail()));
}
executor.shutdown();

结果呢?5分钟搞定。系统不卡了,老板还夸我"效率提升明显"。

所以,什么时候用线程池?

  • 批量处理任务:比如批量发邮件、发短信、导入数据。
  • 耗时的操作:比如用户上传文件,你后台慢慢处理,别让用户一直等"转圈"。
  • 并发请求外部接口:比如你要调10个不同的API,一个一个调太慢,可以并行。
  • 定时任务:比如每天凌晨清理日志,用个线程池跑,别影响主流程。

只要你想"让事情同时干",就可以考虑线程池。

二、常见的使用方式

Java里最常用的几种线程池:

1、固定大小的线程池

java 复制代码
ExecutorService executor = Executors.newFixedThreadPool(5);

适合任务量稳定,不想太多线程吃资源的场景。

2、单线程池

java 复制代码
ExecutorService executor = Executors.newSingleThreadExecutor();

适合想保证任务顺序执行,比如写日志、串行处理队列等场景。

3、可伸缩的线程池

java 复制代码
ExecutorService executor = Executors.newCachedThreadPool();

适合任务突发性强,比如短时间有大量的请求。

4、自定义线程池(推荐)

java 复制代码
ExecutorService executor = new ThreadPoolExecutor(
    5,      // 核心线程数
    10,     // 最大线程数
    60L,    // 空闲线程存活时间
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(100) // 任务队列
);

自己控制线程更安全。如果你怕系统扛不住,可以限制最大数量。

三、Spring里的@Async,其实底层就是线程池

很多人经常用 @Async,但不知道它底层其实也是线程池在干活。

比如:

java 复制代码
@Async
public void sendSms(String phone) {
    // 发短信,耗时操作
    smsService.send(phone, "验证码是1234");
}

只要在方法上加个 @Async,它就会自动扔到线程池里去执行,不会卡主线程。

但要注意的是, @Async默认的线程池是SimpleAsyncTaskExecutor,它每次都会新建线程,用完就丢弃。 并发一高,就直接OOM了。

所以,自己配一个线程池会更安全

java 复制代码
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-pool-");
        executor.initialize();
        return executor;
    }
}

然后在方法上指定用哪个线程池:

java 复制代码
@Async("taskExecutor")
public void sendSms(String phone) {
    // ...
}

这样才更靠谱。

四、还有哪些框架用了线程池?

  1. @Scheduled(定时任务) 它的背后也有线程池,默认是单线程。如果是多个定时任务会排队,你要是想并行跑多个定时任务,也得自己配线程池。
  2. Spring Boot的ApplicationEvent+@EventListener 有些事件监听是异步的,也会用线程池。比如用户注册完,发邮件、发积分、记录日志,可以异步处理。
  3. Tomcat的请求处理 每个HTTP请求,其实是Tomcat线程池里的一个线程在处理。如果你接口慢,有时候也有可能是因为线程不够用。

五、总结

线程池就像奶茶店的员工:他们需要提前准备和合理的分工。 没有最好的线程池,只有最适合业务的线程池。能让我们的程序跑得更快更稳,才能更省心。

本文首发于微信公众号「刘大华的开发笔记」我是大华,专注分享前后端开发的实战笔记。 关注我,少走弯路,一起进步!

相关推荐
葫芦和十三9 分钟前
图解 MongoDB 07|索引类型:七种索引,七种访问形状
后端·mongodb·agent
朦胧之2 小时前
AI 编程-老项目改造篇
java·前端·后端
爱勇宝4 小时前
我做了一个只用来搜歌词的小 App
android·前端·后端
IT_陈寒5 小时前
SpringBoot自动配置坑了我一晚上,原来问题出在这
前端·人工智能·后端
SelectDB6 小时前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
SelectDB6 小时前
秒级弹性、最高降本 70%:SelectDB Serverless 如何重塑云数仓资源效率
大数据·后端·云原生
程序猿大帅6 小时前
别再只当调包侠了:用 Spring AI 落地 Function Calling,我被大模型硬生生砸出了三个大坑
java
PinkSun6 小时前
Spring AI ChatMemory踩坑实录:重启丢数据、Agent丢记忆、对话溢出
后端·ai编程
壹方秘境6 小时前
我用Go语言开发了一个跨平台的HTTPS抓包和调试工具
前端·后端·ios
神秘面具男6 小时前
HarmonyOS 6.0跨端远程控制
前端·后端