前言
Java中的线程池通过复用线程来提高多线程任务的执行效率,而ThreadPoolExecutor就是Java中线程池的核心类之一,它将线程的创建、管理以及资源调度进行了封装,使用ThreadPoolExecutor能够方便地管理多个线程的生命周期和执行。
ThreadPoolExecutor的数据结构
ThreadPoolExecutor包含几个重要的数据结构:workers用来存储线程池中的线程,workQueue用来存储等待执行的任务,mainLock用于保护线程池状态的改变。下面是ThreadPoolExecutor类的定义:
public class ThreadPoolExecutor extends AbstractExecutorService {
// 线程池中实际运行的线程数
private volatile int poolSize;
// 线程池中允许同时存在的最大线程数
private final int maximumPoolSize;
// 线程池任务缓冲队列
private final BlockingQueue<Runnable> workQueue;
// 线程工厂,用于创建新的线程
private final ThreadFactory threadFactory;
// 拒绝策略,用于处理无法处理的任务
private final RejectedExecutionHandler handler;
// ThreadPoolExecutor状态锁
private final Object mainLock = new Object();
// 线程池是否关闭
private volatile boolean shutdown;
// 线程池中所有 worker 数组
private final HashSet<Worker> workers = new HashSet<>();
// 线程池最大任务等待时间,超过此时间未得到线程而造成的任务拒绝处理
private volatile long keepAliveTime;
... // 其他属性和方法
}
线程池的创建和启动的源码分析
ThreadPoolExecutor提供了几个构造函数用于创建线程池对象,在构造函数中会初始化线程池中所需的各项参数,在ThreadPoolExecutor的execute方法中将任务交给线程池进行管理和调度。下面是关键代码:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new SynchronousQueue<Runnable>(), Executors.defaultThreadFactory(), defaultHandler);
this.workQueue = workQueue;
}
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
if (runState == RUNNING && workQueue.offer(command)) { // 将任务添加到任务队列
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
else if (!addIfUnderMaximumPoolSize(command)) // 创建新线程处理任务
reject(command);
}
}
线程池的任务提交的源码分析
ThreadPoolExecutor的任务提交机制采用BlockingQueue来实现,任务首先会进入BlockingQueue,如果线程池中已经存在空闲的线程,则会直接将任务交给空闲线程处理;如果线程池中没有空闲的线程,则会创建新的线程来处理任务。下面是ThreadPoolExecutor中任务提交的关键代码:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
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);
}
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN &&
!(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get();
if (runStateOf(c) != rs)
continue retry;
}
}
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 {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
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);
}
return workerStarted;
}
execute方法接受一个Runnable类型的参数,用于代表任务。如果传入的参数是null,就会抛出NullPointerException异常。在execute方法内部,首先获取线程池的状态和当前线程池中worker的数量。如果worker的数量小于核心线程池大小(corePoolSize),那么调用addWorker方法启动新的线程去执行该任务,接着返回。如果worker的数量已经达到或超过核心线程池大小,并且线程池此时处于运行状态(RUNNING),那么将这个任务添加到阻塞队列中以便后续处理。否则,如果阻塞队列已满,那么会尝试使用addWorker方法创建worker,如果创建失败就会调用reject方法进行拒绝处理。
addWorker方法的作用是添加一个新的Worker线程到线程池中,用于执行任务。该方法首先获取整个线程池的状态和worker数量,如果线程池处于SHUTDOWN或STOP状态,或者阻塞队列非空且firstTask为null,则不再添加worker,并返回false。其次,如果当前worker数量已经等于线程池最大容量或者已达到核心线程池大小(如果core参数为true),也不再添加worker。最后,如果成功增加worker计数,那么创建一个新的Worker对象(传入的是firstTask),加入到workers列表中,然后使用线程池的锁(mainLock)加锁,将largestPoolSize设置为最新的线程池大小,并启动新的线程进行任务执行。
线程池的异常处理的源码分析
ThreadPoolExecutor线程池的异常处理机制包括两个方面:一是线程池内部可能会产生异常,需要进行自我保护和恢复;二是线程池的任务执行过程中可能会因为执行任务代码发生异常而导致线程池出现问题。
线程池内部的异常处理包括在addWorker方法中对worker数量限制的检查,如果超出了capacity或者corePoolSize/maximumPoolSize,就会返回false。此外,在Worker.run方法内部,还有try-catch语句来捕获任务代码内部的异常并处理。当任务执行出现异常时,该线程会停止运行,从workers列表中移除并尝试创建新的线程来替换它。
线程池中的任务执行异常处理则需要通过RejectedExecutionHandler接口来实现。线程池默认的拒绝策略是AbortPolicy,即直接抛出RejectedExecutionException异常。该异常会被runWorker方法捕获并调用processWorkerExit方法进行处理,其中主要是将异常打印输出到控制台。除此之外,用户也可以自定义RejectedExecutionHandler来处理任务执行异常,从而实现自定义的异常处理策略。
综上,ThreadPoolExecutor线程池的异常处理机制主要包括内部自我保护和恢复机制以及用户自定义的任务执行异常处理机制。通过在代码中使用try-catch语句来捕获任务执行内部的异常,并且定义RejectedExecutionHandler接口来实现自定义的异常处理策略。
学习ThreadPoolExcutor的好处
-
学习ThreadPoolExecutor的源码可以带来以下好处:
-
更深入地理解线程池的工作原理和内部机制,从而更好地使用和优化线程池。
-
掌握线程池的异常处理机制,预防和解决线程池在任务执行过程中产生的异常情况。
-
了解线程池的实现细节,例如如何添加或删除线程、如何保证任务的有序处理等,从而提高编程技巧和代码质量。
-
理解并扩展JDK提供的线程池实现,在特定的业务场景下可以对线程池进行个性化定制,满足自身应用的需求。
-
深入掌握线程池的源码还可以为后续研究和实践相关技术打下坚实的基础,例如并发编程,分布式系统等。