1. 核心逻辑概述

ThreadPoolExecutor是Java中批量执行任务的核心类,它通过线程池可以有效地管理和复用线程,从而提高系统效率,减少资源消耗。在ThreadPoolExecutor类中,有几个关键的成员变量对于理解其运行机制至关重要:

  • corePoolSize:线程池的基本大小。
  • maximumPoolSize:线程池最大容量。
  • workQueue:用于缓存待执行任务的阻塞队列。
  • keepAliveTime:非核心线程空闲时的存活时间。
  • threadFactory:用于创建新线程的工厂。

线程池的工作过程大致如下: file

  1. 新提交的任务首先会尝试添加到workQueue中。
  2. 如果workQueue已满,且当前运行的线程数量小于maximumPoolSize,则会创建新的线程来处理任务。
  3. 如果线程数量达到maximumPoolSize且队列也已满,那么采取拒绝策略处理新提交的任务。

ThreadPoolExecutor处理任务的高效性与这些成员变量和它们之间的互动密切相关。

2. execute(Runnable)方法

execute方法是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);
}

当一个任务被提交时,ThreadPoolExecutor首先判断当前活跃的线程数是否小于corePoolSize。如果是,尝试创建一个新的工作线程。如果工作队列未满,任务将被加入队列中。如果工作队列已满,会尝试创建新线程(直到达到maximumPoolSize)。如果创建线程失败,任务将被拒绝。

3. addWorker(Runnable, boolean)方法

addWorker是ThreadPoolExecutor中用于添加新工作线程的关键方法。其源码精简版如下:

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        // 检查线程池状态和是否超过最大容量
        if (!addWorkerConditionsMet(c, core))
            return false;
        
        if (compareAndIncrementWorkerCount(c))
            break retry;
        // 如果CAS操作失败,重试
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        final ReentrantLock mainLock = this.mainLock;
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            mainLock.lock();
            try {
                // 省略了锁定状态下的一些判断和添加工作线程的操作
                workerStarted = true;
            } finally {
                mainLock.unlock();
            }
            if (workerStarted) {
                t.start();
                workerAdded = true;
            }
        }
    } finally {
        if (!workerAdded)
            addWorkerFailed(w);
    }
    return workerAdded;
}

这个方法首先检查线程池状态,尝试通过CAS增加工作线程的计数,随后创建并启动新线程。如果在添加工作线程过程中失败,则调用addWorkerFailed进行异常处理和资源回收。

4. addWorkerFailed(Worker)方法

方法addWorkerFailed是在添加工作线程失败时的清理方法,它负责处理工作线程添加失败后的资源回收和状态恢复工作。当addWorker中的线程启动失败或在初始化过程中异常退出时,这个方法会被调用。

private void addWorkerFailed(Worker w) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null) {
            workers.remove(w);
        }
        decrementWorkerCount();
        tryTerminate();
    } finally {
        mainLock.unlock();
    }
}

在该方法中,首先获取到主锁mainLock以同步访问线程池的状态。如果Worker对象不为null,则从工作线程集合workers中移除该对象。随后减少工作线程计数,并尝试对线程池进行终止操作。这些步骤计入错误处理逻辑,确保线程池状态一致且没有资源泄漏。

5. 拒绝策略

ThreadPoolExecutor提供了几种标准的拒绝策略用以处理当线程池已满且工作队列也满时提交的新任务。Java提供的拒绝策略包括:

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

几种常见的实现如下:

  • AbortPolicy:直接抛出异常。
  • CallerRunsPolicy:调用任务的run()方法,用调用者的线程执行任务。
  • DiscardPolicy:悄无声息地丢弃任务,不做任何处理。
  • DiscardOldestPolicy:丢弃队列中最老的一个任务,并尝试再次提交。

每种策略对应不同的业务场景和需求,开发人员可以根据需要选择或实现自己的拒绝策略。为了详细描述源码实现,我们以AbortPolicy为例:

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());
    }
}

当使用AbortPolicy时,如果线程池的工作线程数量达到最大值且队列也满,则会抛出RejectedExecutionException,告知调用者任务无法被执行。