Java 线程池(ThreadPoolExecutor)源码解析
Java 线程池(ThreadPoolExecutor)源码解析
一、前言
在高并发编程中,合理使用线程池可以有效提高系统的并发能力,降低线程创建和销毁的开销,并提升资源利用率。ThreadPoolExecutor 是 Java 线程池的核心实现类,我们今天将深入剖析其源码,理解线程池的工作机制。
二、线程池核心参数
在 ThreadPoolExecutor 源码中,线程池的核心参数如下:
public class ThreadPoolExecutor extends AbstractExecutorService {
// 线程池状态 + 线程数量 控制变量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 线程池的状态位
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// 线程池的基本参数
private volatile int corePoolSize; // 核心线程数
private volatile int maximumPoolSize; // 最大线程数
private volatile long keepAliveTime; // 线程存活时间
private final BlockingQueue<Runnable> workQueue; // 任务队列
private final RejectedExecutionHandler handler; // 拒绝策略
}
关键点解析
-
ctl变量:使用 高 3 位 存储线程池状态,低 29 位 存储线程数量。 -
corePoolSize和maximumPoolSize影响线程池的扩展能力。 -
workQueue存储等待执行的任务。 -
handler处理任务被拒绝的情况。三、任务提交(
execute方法)
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
// 1. 线程数小于 corePoolSize,创建核心线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) return;
c = ctl.get();
}
// 2. 线程数已达 corePoolSize,任务入队
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command)) reject(command);
else if (workerCountOf(recheck) == 0) addWorker(null, false);
}
// 3. 队列已满,尝试创建非核心线程
else if (!addWorker(command, false)) reject(command);
}
任务提交流程
- 如果当前线程数小于
**corePoolSize**,创建新线程执行任务。 - 如果线程数已达
**corePoolSize**,任务进入**workQueue**等待执行。 - 如果队列已满,尝试创建非核心线程执行任务。
- 如果非核心线程也无法创建,执行拒绝策略(
**RejectedExecutionHandler**)。
四、线程创建(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.
/**
* 除非是:
* 线程池是 SHUTDOWN 状态;
* 要执行的任务是 null(说明是从队列里拿的任务);
* 并且队列里还有任务;
* 否则拒绝添加线程。
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY || //CAPACITY: 表示线程数最大值(2^29 - 1)
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); // 封装任务为 Worker 对象
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.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable //检查线程是否活着(理论上不能活);
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // 添加完毕,启动线程
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); // 如果添加失败,则调用 addWorkerFailed 方法 回滚
}
return workerStarted;
}
线程创建流程
- 检查线程池状态是否允许创建新线程。
- 如果线程数量未超过
**corePoolSize**或**maximumPoolSize**,创建**Worker**线程。 - 启动线程,执行
**runWorker**方法。
五、任务执行(runWorker 方法)
final void runWorker(Worker w) {
Runnable task = w.firstTask;
try {
while (task != null || (task = getTask()) != null) {
task.run();
}
} finally {
processWorkerExit(w, false);
}
}
任务执行流程
- 执行
**firstTask**任务。 - 不断从
**workQueue**获取任务并执行,直到没有任务。 - 当任务执行完毕,进行线程清理和回收。
六、线程回收(getTask 方法)
private Runnable getTask() {
for (;;) {
int c = ctl.get();
boolean timedOut = false;
if (workerCountOf(c) > corePoolSize) {
Runnable r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
if (r != null) return r;
timedOut = true;
} else {
Runnable r = workQueue.take();
if (r != null) return r;
}
if (timedOut) {
decrementWorkerCount();
return null;
}
}
}
线程回收机制
- 线程池中的线程数大于
**corePoolSize**,则等待**keepAliveTime**后回收线程。 - 如果
**corePoolSize**内的线程,则会一直阻塞等待任务。 - 超时后销毁线程,减少资源占用。
线程销毁(processWorkerExit 方法)
当线程池中的工作线程执行完任务并退出时,线程会进入销毁阶段。此时会调用 processWorkerExit 方法进行线程清理和资源释放。销毁的过程包括以下几个步骤:
private void processWorkerExit(Worker w, boolean completedAbruptly) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 从线程池中移除线程
workers.remove(w);
int c = ctl.get();
// 更新线程池状态
if (runStateOf(c) == TIDYING) {
if (workerCountOf(c) == 0) {
// 所有线程都退出后,线程池进入 TERMINATED 状态
ctl.compareAndSet(c, ctlOf(TERMINATED, 0));
}
} else if (completedAbruptly) {
// 如果线程是异常退出的,尝试重新创建线程
addWorker(null, false);
}
} finally {
mainLock.unlock();
}
}
销毁过程解析
-
移除线程:当工作线程执行完任务并退出时,首先会从
workers集合中移除对应的线程。 -
状态检查:通过检查线程池的状态,确定是否所有线程都已经退出。如果所有线程都已退出,线程池将进入
TERMINATED状态。 -
异常退出处理:如果线程异常退出,线程池会尝试重新创建线程以保证线程池的稳定性。
特别:
ThreadPoolExecutor 中有多个扩展点,允许用户根据具体需求对其行为进行定制。以下是一些主要的扩展点:
1. RejectedExecutionHandler(拒绝策略)
- 作用:当线程池无法处理提交的任务时,
RejectedExecutionHandler负责处理这些任务。默认的拒绝策略是AbortPolicy,即抛出RejectedExecutionException异常。 - 扩展方式:可以实现自己的拒绝策略,控制任务被拒绝时的行为。例如,
CallerRunsPolicy会让提交任务的线程来执行任务,或者通过DiscardOldestPolicy丢弃队列中最旧的任务。 - 常用策略:
AbortPolicy:抛出异常,默认策略。CallerRunsPolicy:由调用线程来执行任务。DiscardPolicy:丢弃当前任务,不抛出异常。DiscardOldestPolicy:丢弃任务队列中最旧的任务。
2. ThreadFactory(线程工厂)
-
作用:
ThreadFactory用于定制线程的创建方式。例如,可以在每个线程的名称中加入自定义前缀,或者为线程设置特定的优先级等。 -
扩展方式:通过实现
ThreadFactory接口,可以自定义线程的创建过程。可以在ThreadFactory中设置线程的名称、守护线程标志、优先级等。 -
示例:
-
ThreadFactory customThreadFactory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName("Custom-Thread-" + thread.getId()); thread.setPriority(Thread.MAX_PRIORITY); return thread; } };3.
BlockingQueue(任务队列)- 作用:
BlockingQueue用于存储任务。线程池中的线程会从队列中获取任务并执行。默认使用的是LinkedBlockingQueue,它会阻塞提交线程,直到队列中有空位。 - 扩展方式:你可以使用不同类型的
BlockingQueue,例如ArrayBlockingQueue、SynchronousQueue,甚至可以自定义队列的行为来优化任务的存储和调度。 - 注意:如果队列的容量有限,可能会影响线程池的性能,特别是当线程池满时。
4.
beforeExecute和afterExecute钩子方法-
作用:这两个方法属于
ThreadPoolExecutor类的ThreadPoolExecutor内部的Worker线程。通过重写这些方法,可以在任务执行前后执行自定义操作。 -
扩展方式:
beforeExecute(Thread t, Runnable r):在任务开始执行之前调用。afterExecute(Runnable r, Throwable t):在任务执行完成后调用,可以用来记录日志或者执行清理工作。
-
示例:
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); executor.setBeforeExecuteHandler((thread, task) -> { System.out.println("Task is about to execute"); }); executor.setAfterExecuteHandler((task, throwable) -> { System.out.println("Task has finished executing"); });5.
ThreadPoolExecutor状态变化监听- 作用:
ThreadPoolExecutor内部通过控制变量ctl来管理线程池的状态,可以监控和响应线程池的状态变化。 - 扩展方式:可以通过观察
ThreadPoolExecutor的状态变化,来执行一些额外的逻辑,例如在TIDYING或TERMINATED状态时执行清理操作。
6.
getQueue方法- 作用:通过
getQueue()方法,可以访问当前线程池的任务队列。默认的BlockingQueue是LinkedBlockingQueue,你可以通过继承ThreadPoolExecutor并重写该方法来更改队列的实现。 - 扩展方式:通过获取队列对象,可以根据队列中的任务数来进行调优,比如动态调整队列大小或对队列进行监控。
- 作用:
- 作用:
记录:
1.线程池启动时 不会立刻创建核心线程,而是 有任务来时才创建,并且核心线程是按需创建的,一开始线程池是空的。
2.在线程池中,如果某个线程执行任务时发生了异常并退出(非正常完成),那么线程池会自动减少线程数,并尝试维持线程池的稳定状态(比如再补一个线程回来)
3.当线程池中的工作线程调用 getTask() 时,系统会根据当前的池状态来判断该线程是否需要退出。如果需要退出,线程会完成当前的任务并退出,线程会自然销毁,资源会被 JVM 自动回收。这种方式避免了显式地调用线程销毁方法,而是通过线程自然结束的方式来管理线程池中的线程生命周期。

浙公网安备 33010602011771号