ThreadPoolExecutor详解

本文源码基于JDK21

简介

线程的创建和销毁都比较耗费资源,所以我们可以通过池化技术去重复利用创建好的线程,在Java中,ThreadPoolExecutor就是池化技术的一种具体实现。

核心流程

我们可以通过ThreadPoolExecutor的构造方法去了解它的核心参数有哪些。

java 复制代码
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.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;

    String name = Objects.toIdentityString(this);
    this.container = SharedThreadContainer.create(name);
}

各个参数的说明如下:

  • corePoolSize:核心线程数量
  • maximumPoolSize:最大线程数量
  • keepAliveTime:线程的存活时间
  • unit:存活时间单位
  • workQueue:等待队列
  • threadFactory:创建线程的工厂
  • handler:任务拒绝策略

源码解析

接下来,从提交任务开始,通过源码去了解ThreadPoolExecutor的运行原理。

提交任务

ThreadPoolExecutor有两种提交任务的方式:

  • submit:可以获取任务的返回值
  • execute:无返回值
java 复制代码
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}
Java 复制代码
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    // 1、运行线程数量小于核心线程数,就新建线程
    // 新建线程的逻辑在addWorker方法中
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 2、核心线程数已满,将任务添加到队列里面
    // 会先判断线程是否还在运行,这里会有个recheck
    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))
        // 4、线程数量大于最大线程数,拒绝任务
        reject(command);
}

addWorker

java 复制代码
private boolean addWorker(Runnable firstTask, boolean core) {
    // 1、通过cas操作去修改线程数量
    retry:
    for (int c = ctl.get();;) {
        // Check if queue empty only if necessary.
        if (runStateAtLeast(c, SHUTDOWN)
            && (runStateAtLeast(c, STOP)
                || firstTask != null
                || workQueue.isEmpty()))
            return false;

        for (;;) {
            if (workerCountOf(c)
                >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateAtLeast(c, SHUTDOWN))
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 2、新建线程去执行任务
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int c = ctl.get();

                if (isRunning(c) ||
                    (runStateLessThan(c, STOP) && firstTask == null)) {
                    if (t.getState() != Thread.State.NEW)
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    workerAdded = true;
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 3、启动线程
                container.start(t);
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

线程创建在第2步,线程的启动入口在第3步。Worker核心代码如下:

java 复制代码
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final Thread thread;
    
    Runnable firstTask;
    
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    
    public void run() {
        runWorker(this);
    }
}

Worker实现了Runnable接口,在其构造方法内新建了线程,并把自己作为新建线程的参数,当线程启动的时候,就会执行Workerrun方法。接下来看看ThreadPoolExecutor#runWorkder方法:

java 复制代码
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    // 标志该线程是正常完成任务还是异常终止了
    boolean completedAbruptly = true;
    try {
        // 循环从队列里面拉取任务并执行
        while (task != null || (task = getTask()) != null) {
            w.lock();
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                try {
                    task.run();
                    afterExecute(task, null);
                } catch (Throwable ex) {
                    afterExecute(task, ex);
                    throw ex;
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

核心其实就是一直循环去执行任务,任务完成之后就从队列里面拉取任务;而且还提供了beforeExecuteafterExecute两个钩子方法,我们可以利用这两个方法在任务执行前/后去执行我们想要执行的逻辑。

需要注意getTask方法,当它返回null的时候,循环会终止,这时会走到processWorkerExit方法。

java 复制代码
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();

        // Check if queue empty only if necessary.
        if (runStateAtLeast(c, SHUTDOWN)
            && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        // 只有运行线程数大于核心线程数或者允许核心线程超时
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

可以看到,只有当运行线程数量大于核心线程数或者允许核心线程超时,且从队列中获取任务超时时,getTask才会返回null,接下来看看ThreadPoolExecutor#processWorkerExit方法。

java 复制代码
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        // 1、移除worker
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    tryTerminate();

    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && !workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}

worker被移除,在gc的时候该线程就会被回收,通过这种方式来完成对线程的销毁。

拒绝策略

默认有四种拒绝策略,也可以自己实现RejectedExecutionHandler接口去定义自己想要的拒绝策略。

  • CallerRunsPolicy:提交任务的线程去执行该任务
java 复制代码
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
    }
}
  • AbortPolicy:抛出RejectedExecutionException异常
java 复制代码
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() 
                                         " rejected from " +
                                         e.toString());
}
  • DiscardPolicy:丢弃任务,不做任何处理
java 复制代码
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
  • DiscardOldestPolicy:丢弃最老的任务,然后再次提交本次任务
java 复制代码
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        e.getQueue().poll();
        e.execute(r);
    }
}
相关推荐
C4程序员19 分钟前
北京JAVA基础面试30天打卡08
java·开发语言·面试
货拉拉技术27 分钟前
XXL-JOB参数错乱根因剖析:InheritableThreadLocal在多线程下的隐藏危机
java·分布式·后端
桃源学社(接毕设)35 分钟前
基于Django珠宝购物系统设计与实现(LW+源码+讲解+部署)
人工智能·后端·python·django·毕业设计
God-Hrh36 分钟前
JVM运维
java·开发语言·jvm
鹿导的通天塔37 分钟前
高级RAG 00:检索增强生成(RAG)简介
人工智能·后端
xuejianxinokok1 小时前
解惑rust中的 Send/Sync(译)
后端·rust
Siler1 小时前
Oracle利用数据泵进行数据迁移
后端
mjy_1111 小时前
Linux下的软件编程——文件IO
java·linux·运维
用户6757049885021 小时前
3分钟,手摸手教你用OpenResty搭建高性能隧道代理(附完整配置!)
后端
进阶的小名1 小时前
@RequestMapping接收文件格式的形参(方法参数)
java·spring boot·postman