一、前言:为什么线程池如此重要?
在现代高并发应用中,线程池是Java并发编程的核心组件。据统计,合理使用线程池可以提升系统性能30%-50%,同时避免资源耗尽导致的系统崩溃。阿里巴巴Java开发手册中明确要求:线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
二、线程池的核心价值与优势
2.1 为什么需要线程池?
-
降低资源消耗:减少线程创建和销毁的开销(线程创建约消耗1MB内存,创建时间约5ms)
-
提高响应速度:任务到达时无需等待线程创建
-
提高线程可管理性:统一分配、监控和调优
-
防止资源耗尽:避免无限制创建线程导致OOM
2.2 线程池的适用场景
-
大量短期异步任务
-
高并发请求处理
-
定时任务执行
-
资源受限环境
三、线程池体系结构深度解析
3.1 Executor框架整体架构
// 核心接口关系
Executor → ExecutorService → AbstractExecutorService → ThreadPoolExecutor
3.2 ThreadPoolExecutor核心构造参数
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
四、线程池工作原理与执行流程
4.1 任务执行流程
graph TD
A[提交任务] --> B{核心线程数是否已满?}
B -- 否 --> C[创建核心线程执行任务]
B -- 是 --> D{工作队列是否已满?}
D -- 否 --> E[任务入队列等待]
D -- 是 --> F{线程数是否达到最大值?}
F -- 否 --> G[创建非核心线程执行任务]
F -- 是 --> H[执行拒绝策略]
4.2 execute()方法源码解析
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) // 1. 创建核心线程
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) { // 2. 任务入队
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false)) // 3. 创建非核心线程
reject(command); // 4. 执行拒绝策略
}
五、线程池关键组件详解
5.1 工作队列(BlockingQueue)
|
队列类型 |
特点 |
适用场景 |
|---|---|---|
|
ArrayBlockingQueue |
有界队列,FIFO |
流量可控场景 |
|
LinkedBlockingQueue |
可选有界,FIFO |
吞吐量优先 |
|
SynchronousQueue |
不存储元素,直接传递 |
高响应要求 |
|
PriorityBlockingQueue |
优先级队列 |
任务优先级调度 |
|
DelayedWorkQueue |
延迟队列 |
定时任务 |
5.2 拒绝策略(RejectedExecutionHandler)
// 1. AbortPolicy(默认策略:抛出异常)
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task rejected");
}
// 2. CallerRunsPolicy(调用者执行)
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run(); // 由提交任务的线程执行
}
}
// 3. DiscardPolicy(静默丢弃)
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 什么都不做,直接丢弃
}
// 4. DiscardOldestPolicy(丢弃最旧任务)
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll(); // 丢弃队列头部的任务
e.execute(r); // 重新尝试执行
}
}
5.3 线程工厂(ThreadFactory)
public class CustomThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public CustomThreadFactory(String poolName) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = poolName + "-pool-" + poolNumber.getAndIncrement() + "-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
t.setDaemon(false);
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
六、Executors工具类提供的线程池
6.1 四种常用线程池
// 1. 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(10);
// 2. 单线程线程池
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
// 3. 可缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 4. 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
6.2 为什么不建议使用Executors?
-
FixedThreadPool:使用无界队列,可能堆积大量任务导致OOM
-
SingleThreadExecutor:同样使用无界队列
-
CachedThreadPool:最大线程数为Integer.MAX_VALUE,可能创建大量线程
-
ScheduledThreadPool:同样使用无界队列
最佳实践:根据业务场景手动创建ThreadPoolExecutor
七、线程池配置最佳实践
7.1 参数配置原则
// CPU密集型任务
int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
// IO密集型任务
int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
// 混合型任务
int corePoolSize = Runtime.getRuntime().availableProcessors() * 1.5;
7.2 自定义线程池示例
public class CustomThreadPool {
private static final int CORE_POOL_SIZE = 10;
private static final int MAX_POOL_SIZE = 20;
private static final long KEEP_ALIVE_TIME = 60L;
private static final int QUEUE_CAPACITY = 1000;
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new CustomThreadFactory("business-pool"),
new CustomRejectedExecutionHandler()
);
// 预启动所有核心线程
static {
executor.prestartAllCoreThreads();
}
}
7.3 线程池监控
public class ThreadPoolMonitor {
public static void monitor(ThreadPoolExecutor executor) {
System.out.println("核心线程数: " + executor.getCorePoolSize());
System.out.println("当前线程数: " + executor.getPoolSize());
System.out.println("最大线程数: " + executor.getMaximumPoolSize());
System.out.println("活跃任务数: " + executor.getActiveCount());
System.out.println("已完成任务数: " + executor.getCompletedTaskCount());
System.out.println("队列大小: " + executor.getQueue().size());
System.out.println("队列剩余容量: " + executor.getQueue().remainingCapacity());
}
}
八、线程池源码深度分析
8.1 Worker内部类实现
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread; // 真正执行任务的线程
Runnable firstTask; // 初始任务
Worker(Runnable firstTask) {
setState(-1); // 抑制中断直到runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this); // 核心执行方法
}
}
8.2 runWorker方法核心逻辑
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 允许中断
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
try {
task.run(); // 执行任务
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
8.3 getTask()方法:线程回收关键
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runState(c);
// 检查线程池是否关闭
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 是否允许核心线程超时 或者 当前线程数超过核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 根据timed选择poll或take
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
九、线程池的关闭与优雅终止
9.1 关闭方式对比
// 1. shutdown(): 温和关闭,不再接受新任务,继续处理队列中的任务
executor.shutdown();
// 2. shutdownNow(): 立即关闭,尝试中断所有线程,返回未执行任务列表
List<Runnable> unfinishedTasks = executor.shutdownNow();
// 3. awaitTermination(): 等待线程池完全终止
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
// 超时后强制关闭
executor.shutdownNow();
}
9.2 优雅关闭最佳实践
public class GracefulShutdown {
public static void shutdownGracefully(ThreadPoolExecutor executor) {
executor.shutdown(); // 禁止新任务提交
try {
// 等待60秒
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 取消当前执行的任务
// 再次等待60秒
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未能正常关闭");
}
}
} catch (InterruptedException ie) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
十、面试常见问题与深度解答
10.1 基础原理类
Q1: 线程池的execute()和submit()方法有什么区别?
-
execute()用于提交不需要返回值的任务
-
submit()用于提交需要返回值的任务,返回Future对象
-
submit()内部将Runnable封装为FutureTask
Q2: 核心线程会不会被回收?
-
默认情况下核心线程不会超时回收
-
设置allowCoreThreadTimeOut(true)后,核心线程也会超时回收
10.2 实战应用类
Q3: 如何合理配置线程池参数?
-
CPU密集型:corePoolSize = CPU核数 + 1
-
IO密集型:corePoolSize = CPU核数 * 2
-
混合型:通过压测确定最优值
-
队列选择:根据业务特点选择有界或无界队列
Q4: 线程池中的线程出现异常会怎样?
-
如果任务抛出异常,执行该任务的线程会终止
-
线程池会创建新的线程来替代异常终止的线程
-
可以通过afterExecute()方法捕获和处理异常
10.3 高级原理类
Q5: 线程池如何实现线程复用?
-
通过Worker内部类封装线程和执行逻辑
-
循环从工作队列中获取任务执行
-
通过getTask()方法实现线程的超时回收
Q6: 为什么推荐使用有界队列?
-
防止任务无限堆积导致OOM
-
提供背压机制,当队列满时触发拒绝策略
-
便于系统监控和问题排查
2069

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



