Java 线程池学习笔记
Java 线程池学习笔记:入门与应用
Java开发的都逃不开一个特性,多线程编程,还有一个东西叫线程池。以前写程序,遇到需要并发处理任务的时候,总是直接 new Thread(),用多了发现问题特别多。现在学了线程池,感觉像打开了新世界的大门。这篇文章就分享一下我对线程池的简单理解和一些使用心得,希望对大家也有点帮助。
我精心收集了一份10万字的java全面知识总结,包括了java几乎所有的面试基础解读,有兴趣可以获取看看
领取方式
https://drive.uc.cn/s/5d43a76012494
1、什么是线程池?
简单说,线程池就是提前创建好一批线程,然后让这些线程重复利用,去执行各种任务,而不是每次来一个任务就新建一个线程。
你可以把线程池想象成一个工厂里的“工人队伍”。
- 没有线程池之前:每来一个订单(任务),工厂就临时去外面招一个工人(创建线程)。订单完成后,这个工人就被辞退(销毁线程)。
- 有了线程池之后:工厂里一直养着一支固定的工人队伍(线程池)。来订单了,就从队伍里拉一个闲着的工人去做;订单做完了,这个工人不会被辞退,而是回到队伍里等着下一个订单。
为什么要有它呢?
新建和销毁线程是很消耗系统资源的。如果任务很多、很频繁,系统就会一直忙着创建和销毁线程,真正干活的时间反而少了。线程池就是为了解决这个问题,它通过重复利用线程,减少了这些开销,提高了程序的效率和系统的稳定性。
2、能说说工作中线程池的应用吗?
线程池在实际工作中用得非常多,只要是需要并发、异步处理的地方,基本上都能看到它的身影。我简单列举几个常见的应用场景。
场景一:异步任务处理
比如用户下单、发送短信、发送邮件这些操作。
- 下单:用户点击“支付”后,核心流程是扣款和生成订单。但同时,我们还需要给用户发短信通知、生成一份 PDF 电子收据、更新积分等等。
- 应用方式:我们可以把“发送短信”、“生成收据”、“更新积分”这些不影响主流程的操作,扔给线程池去异步执行。主线程很快就能返回给用户“下单成功”的提示,用户体验会更好。
// 假设这是下单成功后的操作
public void handleOrderSuccess(Order order) {
// 1. 核心流程:扣款、记录订单... (同步执行)
// 2. 非核心流程:交给线程池异步处理
// 获取一个 ExecutorService 实例
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交发送短信任务
executor.submit(() -> sendSms(order.getUserId()));
// 提交更新积分任务
executor.submit(() -> updatePoints(order.getUserId(), order.getAmount()));
// 3. 返回结果给用户
System.out.println("订单处理完毕,部分任务已转入后台...");
}
场景二:Web 服务器中的请求处理
一个 Web 服务器要同时处理成百上千个用户请求。
- 应用方式:Web 服务器(比如 Tomcat)内部就是使用线程池来管理这些请求。每当有用户连接进来时,就从线程池里取一个线程去处理这个用户的请求。这样可以保证服务的稳定性和高并发性。
场景三:定时任务和周期性任务
需要定时执行的清理任务、数据同步任务等。
- 应用方式:可以使用 Java 提供的
ScheduledExecutorService,它是线程池的一种,专门用来处理定时任务。比如每天凌晨 2 点执行一次数据库备份。
场景四:批处理
需要处理大量数据,将大任务拆分成多个小任务并发执行。
- 应用方式:比如要处理 1000 万条数据,可以把数据分成 100 份,每份 10 万条,然后把这 100 个小任务提交给线程池并行处理,能大大缩短总耗时。

3、能简单说下线程池的工作流程吗?
线程池的工作流程是它最核心的部分,了解这个流程,就能明白线程池的各种参数是怎么起作用的。
我们可以把线程池想象成一个有门卫的接待大厅。
- 新任务来了(订单来了):一个任务被提交给线程池。
- 看核心工人(
corePoolSize):- 线程池先看一看核心工人(核心线程数)是不是都还没满。
- 如果没满:立马创建一个新的核心线程去执行任务,哪怕当前有空闲的线程。
- 如果满了:进入下一步。
- 看等待区(工作队列
BlockingQueue):- 核心工人满了,线程池就把新任务放到等待区(任务队列)里排队。
- 所有核心线程会不断地从这个等待区里取任务执行。
- 如果等待区满了:进入下一步。
- 看临时工人(
maximumPoolSize):- 等待区也满了,线程池知道现在任务量很大,超过了核心工人的处理能力。
- 它会开始创建临时工人(非核心线程)。但是临时工人的数量不能超过最大线程数。
- 如果临时工人还没到上限:创建临时线程去执行任务。
- 如果临时工人也到上限了:进入下一步。
- 拒绝接收(拒绝策略
RejectedExecutionHandler):- 线程池已经尽力了,核心工人满了,等待区满了,临时工人也满了。它没办法再处理新任务了。
- 这时,线程池会根据你设定的拒绝策略来处理这个任务,比如直接抛出异常、丢弃任务等。
4、线程池的主要参数有哪些?
在 Java 中,我们通常使用 ThreadPoolExecutor 类来创建线程池,它有几个非常重要的参数。理解这些参数是正确配置线程池的关键。
参数列表
| 序号 | 参数名称 | 英文名称 | 作用说明 |
|---|---|---|---|
| 1 | 核心线程数 | corePoolSize |
线程池中常驻的核心线程数量。即使线程空闲,也不会被销毁(除非设置了 allowCoreThreadTimeOut)。 |
| 2 | 最大线程数 | maximumPoolSize |
线程池中能创建的最大线程数量。当任务队列满了,并且当前线程数小于最大线程数时,会创建非核心线程。 |
| 3 | 空闲等待时间 | keepAliveTime |
当线程池中的线程数量大于核心线程数时,这些非核心的空闲线程的最大存活时间。超过这个时间就会被销毁。 |
| 4 | 时间单位 | unit |
keepAliveTime 的时间单位,比如秒、毫秒等。 |
| 5 | 任务队列 | workQueue |
存放待执行任务的阻塞队列。常用的有 ArrayBlockingQueue (有界)、LinkedBlockingQueue (无界或有界) 等。 |
| 6 | 线程工厂 | threadFactory |
用于创建新线程的工厂。可以自定义线程的名称、优先级等。 |
| 7 | 拒绝策略 | handler |
当任务队列和最大线程数都满了,线程池采取的拒绝新任务的策略。 |
配置示例
下面是一个创建线程池的完整代码例子,展示了如何设置这些参数:
import java.util.concurrent.*;
public class ThreadPoolConfig {
public static void main(String[] args) {
// 1. 核心线程数:设置为 2
int corePoolSize = 2;
// 2. 最大线程数:设置为 5
int maximumPoolSize = 5;
// 3. 空闲等待时间:非核心线程空闲 60 秒后销毁
long keepAliveTime = 60;
// 4. 时间单位:秒
TimeUnit unit = TimeUnit.SECONDS;
// 5. 任务队列:使用容量为 10 的 ArrayBlockingQueue (有界队列)
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);
// 7. 拒绝策略:使用 CallerRunsPolicy (由调用者线程执行任务)
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
handler
);
System.out.println("线程池创建完成,开始提交任务...");
// 提交 15 个任务 (2核心 + 10队列 + 3临时 = 15)
for (int i = 1; i <= 15; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行任务 " + taskId);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executor.shutdown();
}
}
关于拒绝策略 (handler)
Java 内置了四种拒绝策略,它们处理超负荷任务的方式不同:
AbortPolicy(默认):直接抛出RejectedExecutionException异常,阻止系统继续运行。CallerRunsPolicy:既不抛弃,也不抛异常。而是将任务回退到调用者线程执行。DiscardOldestPolicy:丢弃队列中最老的(等待时间最长的)任务,然后尝试提交当前任务。DiscardPolicy:默默地丢弃掉新提交的任务,不做任何处理。
更多推荐

所有评论(0)