Java线程池原理分析
Java中的线程池(ThreadPool)是并发编程中的一个重要工具,用于管理和重用多个线程,减少线程创建和销毁的开销。本文通过详细的源码分析,深入探讨Java线程池的实现原理。
1. 线程池的基本概念
线程池是一组可以执行任务的线程集合,通过管理线程的生命周期,提高了资源利用率和程序性能。Java中的线程池由java.util.concurrent包中的ThreadPoolExecutor类实现。
2. 线程池的核心参数
创建ThreadPoolExecutor实例时,需配置以下核心参数:
corePoolSize:核心线程数,即线程池中始终存活的线程数。maximumPoolSize:最大线程数,即线程池中允许创建的最大线程数。keepAliveTime:线程空闲时间,多余的空闲线程在终止前等待新任务的最长时间。unit:时间单位,指定keepAliveTime的单位。workQueue:任务队列,用于存储等待执行的任务。threadFactory:线程工厂,用于创建新线程。handler:拒绝策略,当任务无法执行时的处理方式。
3. 线程池的工作原理
3.1 任务提交与执行
线程池的工作流程如下:
- 任务提交:通过
execute(Runnable task)或submit(Callable<T> task)方法提交任务到线程池。 - 核心线程处理:如果当前线程数少于
corePoolSize,则创建一个新的核心线程来执行任务。 - 任务队列:如果运行的线程数达到
corePoolSize,任务将被放入workQueue中等待执行。 - 非核心线程处理:如果任务队列已满且运行的线程数少于
maximumPoolSize,则创建新的非核心线程来处理任务。 - 拒绝处理:如果任务队列已满且运行的线程数达到
maximumPoolSize,则执行拒绝策略。
3.2 线程池的创建与配置
ThreadPoolExecutor的构造方法如下:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 存活时间的单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler) // 拒绝策略
{
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.workQueue = workQueue;
this.threadFactory = threadFactory;
this.handler = handler;
}
3.3 线程池的状态与控制
线程池通过一个AtomicInteger类型的ctl变量来维护线程池的状态和线程数量。ctl变量的高3位表示线程池的状态,低29位表示线程数量。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
线程池的主要状态包括:
- RUNNING:接受新任务并处理排队任务。
- SHUTDOWN:不接受新任务,但处理排队任务。
- STOP:不接受新任务,不处理排队任务,中断正在执行的任务。
- TIDYING:所有任务已终止,
workerCount为0,转换到此状态后将调用terminated()方法。 - TERMINATED:
terminated()方法执行完毕。
3.4 任务执行流程
ThreadPoolExecutor的execute方法是任务提交的入口:
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);
// 如果线程数为0,则创建新的非核心线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果任务队列已满且线程数未达到最大线程数,则创建新的非核心线程
else if (!addWorker(command, false))
reject(command);
}
3.5 addWorker方法
addWorker方法用于创建并启动一个新线程来执行任务:
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;
}
4. 任务拒绝策略
当线程池无法接受新任务时,会执行拒绝策略。Java提供了四种内置的拒绝策略:
- AbortPolicy:直接抛出
RejectedExecutionException异常。 - CallerRunsPolicy:由提交任务的线程处理该任务。
- DiscardPolicy:直接丢弃任务,不抛出异常。
- DiscardOldestPolicy:丢弃队列中最旧的任务,然后重新尝试执行任务。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
5. 线程池的监控与管理
线程池提供了一些方法来监控和管理线程池的状态:
getPoolSize():返回当前线程池中的线程数。getActiveCount():返回当前正在执行任务的线程数。getCompletedTaskCount():返回已完成的任务总数。getTaskCount():返回任务总数,包括已完成、正在执行和排队等待的任务。
public int getPoolSize() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
return workerCountOf(ctl.get());
} finally {
mainLock.unlock();
}
}
public int getActiveCount() {
final ReentrantLock mainLock = this.mainLock