ThreadPoolExecutor.execute() 核心源码流程解析
                           
天天向上
发布: 2025-04-26 19:12:36

原创
331 人浏览过

如果你真的读过ThreadPoolExecutor.execute() 源码,还能应用到面试上,面试官一定对你另眼相看!


1. 源码入口(基于 JDK 8)

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

第一步:参数校验

if (command == null)
    throw new NullPointerException();
  • 如果提交的任务是 null,直接抛出空指针异常。
  • 目的:保证线程池处理的是有效任务。

第二步:获取当前线程池状态

int c = ctl.get();
  • ctl 是一个高位存线程池运行状态,低位存线程数量的复合变量(int 型,位操作编码)。
  • workerCountOf(c) 获取当前有效线程数量。

第三步:核心线程是否能处理?

if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))
        return;
    c = ctl.get(); // 失败后重新获取状态
}
  • 当前运行线程数小于 corePoolSize
  • 是 → 尝试创建一个新的核心线程来执行这个任务。
  • 调用 addWorker(command, true),参数 true 表示创建核心线程
  • 创建成功,任务就直接交给新线程处理,方法返回,结束。

(注意:如果创建核心线程失败,比如线程池状态变化了,需要继续处理。)


第四步:线程池正在运行且队列能放进去?

if (isRunning(c) && workQueue.offer(command)) {
  • 如果线程池状态是 RUNNING(正常运行中),并且
  • 成功把任务加入到工作队列 workQueue

✅ 说明:

  • 没必要新建线程。
  • 任务会被已有线程从队列取出执行。

第五步:二次校验(防止入队后,线程池关闭)

int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
    reject(command);
else if (workerCountOf(recheck) == 0)
    addWorker(null, false);
  • 任务刚加进队列,线程池可能在瞬间被关闭了(比如被 shutdown() 调用了)。
  • 所以需要再次检查线程池状态。
  • 如果线程池不是运行状态了,而且能把刚加进去的任务移除成功 → 直接拒绝任务。
  • 如果线程池是空的(worker数为 0)→ 创建一个线程去保证至少有线程执行队列里的任务。

第六步:队列放不下,扩充线程(扩展线程)

如果队列满了(offer 失败)怎么办?

else if (!addWorker(command, false))
    reject(command);
  • 不能直接丢弃!
  • 继续尝试创建新的线程来执行这个任务。
  • 调用 addWorker(command, false)false 参数表示创建的是非核心线程(maximumPoolSize 范围内)。
  • 如果加线程也失败(线程池满了或者关闭了) → 调用拒绝策略 reject(command)

【总结一句话版】

execute() 的核心流程是:先看能不能加核心线程 ➔ 加不了就入队列 ➔ 入不了队列就加扩展线程 ➔ 还不行就拒绝任务。

这也是面试常问的线程池执行顺序口诀:

核心线程 ➔ 工作队列 ➔ 非核心线程 ➔ 拒绝处理

(拓展)面试补充:addWorker() 里面干了啥?

如果面试官追问:“addWorker内部是怎么保证线程安全的?”

简单回答就可以:

  • addWorker() 内部用了一把重入锁(mainLock)保护 Worker 的添加。
  • 先检查线程池状态合法。
  • 然后真正启动一个新的 Worker 线程(封装 Runnable,执行 runTask)。
  • 线程安全、状态检查都在里面做到了。

更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *