跳转至

线程池的执行流程

一、execute() 源码流程

public void execute(Runnable command) {
    int c = ctl.get();
    // 1. 当前线程数 < core,直接创建核心线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 2. 核心满了,入队列
    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);
    }
    // 3. 队列满了,创建临时线程
    else if (!addWorker(command, false))
        reject(command);   // 4. 到最大,拒绝
}

二、流程图

新任务
线程数 < corePoolSize? → 创建核心线程执行
  ↓ 否
入队列 workQueue?
  ↓ 成功
队列里等
  ↓ 队列满
线程数 < maximumPoolSize? → 创建临时线程执行
  ↓ 否
拒绝策略 handler

三、addWorker

创建 Worker(包装一个线程),启动:

private boolean addWorker(Runnable firstTask, boolean core) {
    // CAS 增加线程数
    // 创建 Worker,启动线程
    Worker w = new Worker(firstTask);
    Thread t = w.thread;
    t.start();
}

四、Worker 怎么循环取任务

Worker.run() → runWorker():

final void runWorker(Worker w) {
    Runnable task = w.firstTask;
    while (task != null || (task = getTask()) != null) {
        // 执行任务
        task.run();
        task = null;
    }
}

getTask() 从队列 take(),队列空就阻塞。

五、为什么先入队列再开线程

设计: 1. 核心线程够用,不开新线程。 2. 队列能扛住突发流量。 3. 队列满了才开临时线程,避免线程过多。

六、异常处理

任务里抛异常: - execute():异常会抛到调用方,线程终止。 - submit():异常被包装到 Future,get() 时才抛。

高频追问

  • 为什么不直接开 maximum?因为队列能缓冲,避免线程过多。
  • 核心线程能不能被回收?默认不能,allowCoreThreadTimeOut=true 可以。