Java线程池原理分析

124 阅读5分钟

Java线程池原理分析

Java中的线程池(ThreadPool)是并发编程中的一个重要工具,用于管理和重用多个线程,减少线程创建和销毁的开销。本文通过详细的源码分析,深入探讨Java线程池的实现原理。

1. 线程池的基本概念

线程池是一组可以执行任务的线程集合,通过管理线程的生命周期,提高了资源利用率和程序性能。Java中的线程池由java.util.concurrent包中的ThreadPoolExecutor类实现。

2. 线程池的核心参数

创建ThreadPoolExecutor实例时,需配置以下核心参数:

  • corePoolSize:核心线程数,即线程池中始终存活的线程数。
  • maximumPoolSize:最大线程数,即线程池中允许创建的最大线程数。
  • keepAliveTime:线程空闲时间,多余的空闲线程在终止前等待新任务的最长时间。
  • unit:时间单位,指定keepAliveTime的单位。
  • workQueue:任务队列,用于存储等待执行的任务。
  • threadFactory:线程工厂,用于创建新线程。
  • handler:拒绝策略,当任务无法执行时的处理方式。

3. 线程池的工作原理

3.1 任务提交与执行

线程池的工作流程如下:

线程池时序图.svg

  1. 任务提交:通过execute(Runnable task)submit(Callable<T> task)方法提交任务到线程池。
  2. 核心线程处理:如果当前线程数少于corePoolSize,则创建一个新的核心线程来执行任务。
  3. 任务队列:如果运行的线程数达到corePoolSize,任务将被放入workQueue中等待执行。
  4. 非核心线程处理:如果任务队列已满且运行的线程数少于maximumPoolSize,则创建新的非核心线程来处理任务。
  5. 拒绝处理:如果任务队列已满且运行的线程数达到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()方法。
  • TERMINATEDterminated()方法执行完毕。

3.4 任务执行流程

ThreadPoolExecutorexecute方法是任务提交的入口:

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