01 - Java线程池详解:高效并发编程的核心利器
参考资料:https://www.cnblogs.com/damaoa/p/18936921
前提:需要掌握多线程概念
一、什么是线程池
1. 线程池的定义
线程池就像一个"线程工厂",它预先创建一定数量的工作线程并放在"池子"里待命。
当有任务需要执行时,不需要重新创建线程,而是直接从池子里取一个空闲线程来干活。
任务完成后,线程不会被销毁,而是重新回到池子里等待下一个任务。
这就好比一个餐厅,与其每来一个客人就临时招聘一个服务员,不如提前雇好几个服务员待命,这样既节省了招聘成本,又能保证服务质量。
2. 为什么需要线程池
想象一下没有线程池的痛苦:
传统方式的问题:
- 资源浪费严重:每个任务都创建新线程,用完就丢弃,
- 响应速度慢:创建线程需要时间,用户等得不耐烦
- 系统压力大:线程数量无法控制,高并发时可能创建成千上万个线程,系统直接崩溃
- 内存溢出风险:每个线程都要占用内存空间,线程太多直接爆内存
// 反面教材:每次都创建新线程
public void handleRequest(String request) {
new Thread(() -> {
System.out.println("处理请求: " + request);
// 处理业务逻辑...
}).start(); // 用完就销毁,太浪费了!
}
使用线程池的优势:
- 资源复用:线程用完不销毁,循环利用,环保又高效
- 快速响应:线程提前准备好,任务来了立即执行
- 流量控制:限制最大线程数,保护系统不被压垮
- 统一管理:线程的创建、销毁、监控都有专人负责
// 正确做法:使用ThreadPoolExecutor创建线程池
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(100), // 工作队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
public void handleRequest(String request) {
executor.submit(() -> {
System.out.println("处理请求: " + request);
// 处理业务逻辑...
});
}
3. 线程池工作原理
线程池的工作流程可以用一个形象的比喻来理解:
想象线程池是一个快递公司,有以下几个关键角色:
- 核心快递员:公司的正式员工,即使没活干也不会被裁员
- 临时快递员:业务繁忙时临时雇佣的员工
- 任务仓库:存放待配送包裹的仓库
- 人事部门:负责处理超出处理能力的订单
4. 线程池的执行流程
1)如果workerCount < corePoolSize ==> 创建线程执行提交的任务
2)如果workerCount >= corePoolSize && 阻塞队列未满 ==> 添加至阻塞队列,等待后续线程来执行提交地任务
3)如果workerCount >= corePoolSize && workerCount < maxinumPoolSize && 阻塞队列已满 ==> 创建非核心线程执行提交的任务
4)如果workerCount >= maxinumPoolSize && 阻塞队列已满 ==> 执行拒绝策略
4.1 提交任务
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 工作线程数量 < corePoolSize => 直接创建线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 工作线程数量 >= corePoolSize && 线程池处于运行状态 => 将任务添加至阻塞队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
/**
* 为什么需要double check线程池的状态?
* 在往阻塞队列中添加任务地时候,有可能阻塞队列已满,需要等待其他的任务移出队列,在这个过程中,线程池的状态可能会发生变化,所以需要doublecheck
* 如果在往阻塞队列中添加任务地时候,线程池地状态发生变化,则需要将任务remove
*/
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
4.2 addWorker 创建线程加入线程池
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 线程池状态处于非RUNNING状态,添加worker失败
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 判断线程池中线程数量是否处于该线程池允许的最大线程数量,如果允许创建线程,则cas更新线程池中线程数量,并退出循环检查,执行下面创建线程地逻辑
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 创建线程
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.

最低0.47元/天 解锁文章
169万+

被折叠的 条评论
为什么被折叠?



