深入理解Java线程池-ThreadPoolExecutor实现原理(一)

ThreadPoolExecutor

JUC提供的线程池ThreadPoolExecutor是处理并发任务常用的工具,他提供了对线程资源、任务资源的管理方法,并方便的处理并发任务。同时,封装也遮蔽了其实现原理,对原理不清楚而导致的线程数设置不合理、队列容量过大等导致资源调度不合理、内存溢出等线上问题。此外,线程池也是面试中热门话题,掌握线程池的实现原理并合理使用是非常必要的。

关于ThreadPoolExecutor的源码解析文章不少,但关于其本质、设计思想和设计模型方面文笔较少。本文先从线程池的whatwhy讲述,再结合源码分析实现原理,也就是how,最后结合线上环境的线程池使用不当事故,分析线程池使用过程中的不足和相应的解决办法。

前言

线程池是什么

线程池是一种基于池化思想 实现的线程管理工具,其解决的核心问题就是资源管理 问题,通过缓存线程和复用线程方式,实现空间换时间的设计理念。

线程池内部本质是生产-消费者 模型,将线程任务解耦开来。线程管理充当生产者,任务管理充当消费者,任务到来分配给线程处理(或缓存到阻塞队列),线程处理完任务后继续获取新任务继续执行,直至获取不到任务。

为什么要使用线程池

换言之,线程池解决了什么问题,带来了什么好处。

在并发的环境下,无法确定任意时刻有多少任务需要并发处理,需要创建多少线程资源,被创建的线程缺少统一管理,这样的环境下可能出现如下问题:

  1. 线程创建开销很大,频繁的创建/销毁线程带来的额外消耗可能非常大
  2. 存在无限制申请线程资源,线程资源耗尽的不可控风险
  3. 线程资源分散,无法有效管理和监控,增加系统的不稳定性

以上问题的核心是:资源管理 ,为解决这个问题,线程池采用了池化思想

Java中,池化思想是一种通过创建和管理可重复使用的对象池来提高性能和资源利用率的编程思想。核心概念是需要申请资源时从池中获取,而非创建新对象,使用完毕后归还池中以便复用。

池化具有如下优点:

  1. 降低资源消耗:避免重复创建和销毁资源,减少系统开销。
  2. 提高响应速度:重复使用已存在的资源,可以节省资源申请开销时间,提高响应速度。
  3. 提高资源的可管理性:对资源集中管理,可以统一分配、调优和监控,避免无限制申请耗尽资源。

当然也引入了一些缺点:

  1. 内存消耗:池化是通过空间换时间,为了维护一定数量的资源,需要占用相应的空间。
  2. 复杂性:资源的分配、获取和释放,资源池的并发访问和线程安全性等增加了额外的复杂性和维护成本。
  3. 潜在资源泄漏:资源未能正确释放或处理异常,可能出现资源泄漏。

除了线程池使用了池化思想,计算机中还有不少使用了该思想:

  • 连接池:预先申请数据库连接,使用时从池中获取空闲连接,释放放回池中。避免频繁创建/断开数据库连接,提高数据库访问效率。
  • 实例池(对象池):维护一定对象实例,避免频繁创建/销毁对象,减少创建和垃圾回收开销,提高性能。
  • 常量池:Java中常量池实质上也是对象池
  • 内存池:预先申请内存,从池中申请/回收内存,提升速度。

线程池如何实现

在了解了线程池whatwhy之后,我们通过用JUC的线程池ThreadPoolExecutor来探索下线程池的内部实现原理。

总体设计

我们基于JDK1.8来分析ThreadPoolExecutor设计和实现原理,首先查看UML类图,了解继承关系。

Executor

ThreadPoolExecutor的顶层实现接口是Executor,该接口的描述:

An object that executes submitted Runnable tasks. This interface provides a way of decoupling task submission from the mechanics of how each task will be run, including details of thread use, scheduling, etc

该接口提供了一种思想:将任务提交任务运行 解耦,用户无需关心线程如何使用、调度等,只需提供Runnable任务即可。

java 复制代码
/**
    * Executes the given command at some time in the future.  The command
    * may execute in a new thread, in a pooled thread, or in the calling
    * thread, at the discretion of the {@code Executor} implementation.
    */
void execute(Runnable command);

ExecutorService

An Executor that provides methods to manage termination and methods that can produce a Future for tracking progress of one or more asynchronous tasks.

ExecutorService继承Executor并增加了一些功能:

  1. 管控线程池:例如shutdownshutdownNow用于终止线程池运行。
  2. 增加执行任务能力:提供为一个或多个异步任务生成Future追踪进度的方法。

ExecutorService的方法也印证了上述两点。

AbstractExecutorService

AbstractExecutorService是上层抽象类,实现了ExecutorService部分方法

ThreadPoolExecutor

ThreadPoolExecutor类提供一个可扩展的线程池实现,也是最复杂的部分,主要实现了:

  • 线程池生命周期管理
  • 资源(线程/任务)管理与维护
  • 调度线程和任务,使两者结合实现并发处理任务

从总体角度上来观察运行机制

正如前面所述,ThreadPoolExecutor内部是一个生产-消费 模型,将任务线程解耦。

我们将ThreadPoolExecutor拆分为三大部分,一一探索运行机制

  • 生命周期管理
  • 线程管理
  • 任务管理

生命周期管理

ctl

java 复制代码
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ctl是一个原子整数,其包含两部分:

  • workerCount:有效线程数,占用低位29位,限制最大值为2^29-1
  • runState:线程池运行状态,占用高位3位

这两个状态维护到一个变量中,各占据不同位置,互不影响。这样做避免维护两个变量,在并发环境下,增加复杂性。相应的,也提供了方法获取runStateworkCount,都是使用位运算,效率更高。

java 复制代码
private static final int COUNT_BITS = Integer.SIZE - 3;    //29位
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;    //2^29-1
//获取runState
private static int runStateOf(int c)     { return c & ~CAPACITY; }    //高3位
//获取workCount
private static int workerCountOf(int c)  { return c & CAPACITY; }    //低29位
//ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }    //高三位|低29位

runState

runState有5种状态:

java 复制代码
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

这些状态的值顺序非常重要,方便他们之间直接比较,在后续能看到不少状态之间的直接比较。

  1. RUNNING: Accept new tasks and process queued tasks
  2. SHUTDOWN: Don't accept new tasks, but process queued tasks
  3. STOP: Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks
  4. TIDYING: All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method
  5. TERMINATED: terminated() has completed

构造方法

java 复制代码
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ThreadPoolExecutor实例创建时,默认状态为RUNNING,进入生命周期的第一个节点,先从类的构造函数探索初始化时做了哪些操作。

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.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

构造函数的参数含义:

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数(核心线程数+非核心线程数)
  • keepAliveTime:非核心线程在空闲时最长存活时间
  • workQueue:线程池工作队列,用来保存等待执行任务的阻塞队列
  • threadFactory:线程工厂
  • handler:线程池饱和拒绝策略

任务管理

任务分配

用户提交的任务进入线程池首先来到任务分配 节点,该阶段是任务管理的核心处理逻辑。ThreadPoolExecutor的任务分配由execute方法实现,此接口是顶层接口Executor定义的,目的将任务提交与任务运行解耦。

java 复制代码
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    //1. 小于核心线程
    if (workerCountOf(c) < corePoolSize) {
        //新建核心线程并直接处理该任务
        if (addWorker(command, true))
            return;
        //添加失败,说明发生变化,获取最新值
        c = ctl.get();
    }
    //2.大于核心线程,尝试加入等待队列
    if (isRunning(c) && workQueue.offer(command)) {
        //成功置入队列,双重校验
        int recheck = ctl.get();
        //线程池shutdown,将之前成功加入队列的任务移除
        if (! isRunning(recheck) && remove(command))
            //移除成功,执行拒绝策略,拒绝此任务
            reject(command);
        //兜底,保证至少有一个线程处理任务,避免阻塞
        else if (workerCountOf(recheck) == 0)
            //新建非核心线程
            //第一个参数为null,只创建一个线程,不绕过队列将任务交给改线程处理(走队列)
            addWorker(null, false);
    }
    //3.!isRunning || 阻塞队列已满
    //新建非核心线程并直接处理任务
    else if (!addWorker(command, false))
        //shutdown或线程达到maxPoolSize,则拒绝
        reject(command);
}

执行流程:

  1. workerCount<corePoolSize,则创建核心线程,并将此任务交由新增的核心线程处理
  2. workerCount>=corePoolSize,且阻塞队列未满,则将任务置入阻塞队列
  3. workerCount>=corePoolSize && workerCount<maxPoolSize,且阻塞队列已满,则创建非核心线程并处理该任务
  4. workerCount>=corePoolSize && workerCount>=maxPoolSize,且阻塞队列已满,则执行拒绝策略

流程图:

任务入列

前面了解到,ThreadPoolExecutor内部实现是一个生产-消费模型,阻塞队列便是将生产者和消费者解耦的关键,生产者将任务存储到阻塞队列中,消费者从阻塞队列中获取任务交与线程处理。阻塞队列缓冲任务,也是线程池实现管理任务的关键。

ThreadPoolExecutor中使用存储Runnable类型的BlockingQueue作为保存任务和将任务移交给工作线程的队列

java 复制代码
/**
 * The queue used for holding tasks and handing off to worker threads.
 */
private final BlockingQueue<Runnable> workQueue;

A Queue that additionally supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element. BlockingQueue implementations are designed to be used primarily for producer-consumer queues

BlockingQueue主要用于生产者-消费者场景,是一种支持两种额外操作的队列:

  • 队列为空时,获取元素的线程等待队列变为非空(从队列获取元素阻塞)
  • 队列为满时,存储元素的线程等待队列变为可用(往队列添加元素阻塞)

BlockingQueue实现类:

  • ArrayBlockingQueue:由数组结构组成的有界阻塞队列,按FIFO(先进先出)规则对元素进行排序
  • LinkedBlockingQueue:由链表结构组成的有界阻塞队列,按FIFO(先进先出)规则对元素进行排序,队列默认长度Integer.MAX_VALUE
  • PriorityBlockingQueue:支持优先级排序的无界阻塞队列,与类PriorityQueue排序规则相同
  • DelayQueue:使用PriorityQueue实现的延迟无界阻塞队列,其中的元素只能在其延迟过期时被获取
  • SynchronousQueue:不存储元素的阻塞队列
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列

任务获取

获取任务有两处:1. 任务分配时,直接执行任务;2. 从阻塞队列中获取任务。

队列中获取任务在getTask方法中:

java 复制代码
private Runnable getTask() {
    //上次从队列中获取任务是否超时标识(true表明执行poll超时了也没获取到任务)
    boolean timedOut = false; // Did the last poll() time out?

    //死循环
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        //等价于(rs>=STOP || (rs>=SHUTDOWN && workQueue.isEmpty()))
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            //减少工作线程数,返回null
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        //是否超时控制
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        //wc > maximumPoolSize , 可能调用setMaximumPoolSize更改了最大线程数。
        //(wc > 1 || workQueue.isEmpty()):wc-1,减少的1.wc>corePoolSize,也可能是2.corePoolSize
        if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
            //当(timed && timedOut)==true时,上一次阻塞的时间刚好是keepAliveTime.所以线程最长存活keepAliveTime后可能被回收
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            //允许超时?poll获取,超时控制时长keepAliveTime : take获取,队伍为空则一直阻塞
            Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
            if (r != null)
                return r;
            //r == null,则poll执行超时,超时标识置为true
            timedOut = true;
        } catch (InterruptedException retry) {
            //中断
            timedOut = false;
        }
    }
}

执行流程:

  1. 判断线程池状态,停止运行时,则wc-1
  2. 判断当前线程是否过多,过多则wc-1
  3. 根据线程是否可回收,执行限时超时或阻塞方法获取队列元素

获取任务流程图:

任务拒绝

从任务分配中了解到,当满足:workerCount>=corePoolSize && workerCount>=maxPoolSize,且阻塞队列已满,则执行拒绝策略,从而保护线程池。

java 复制代码
/**
  * Handler called when saturated or shutdown in execute.
  */
private volatile RejectedExecutionHandler handler;
//默认拒绝策略
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

拒绝策略实现RejectedExecutionHandler接口便可,可以实现该接口实现自定义的拒绝策略。JDK已经实现了四种拒绝策略。

AbortPolicy

默认策略,直接抛出异常丢弃任务。实际处理业务时,超过线程池负载时抛出异常,有助于及时通过异常发现问题。

java 复制代码
/**
    * A handler for rejected tasks that throws a {@code RejectedExecutionException}.
    */
public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }

    /**
        * Always throws RejectedExecutionException.
        */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                " rejected from " +
                e.toString());
    }
}

DiscardPolicy

什么也不做,直接丢弃任务。该策略会使得难以发现系统异常,无关要紧的可以考虑该策略。

java 复制代码
/**
    * A handler for rejected tasks that silently discards the rejected task.
    */
public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }

    /**
        * Does nothing, which has the effect of discarding task r.
        */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

CallerRunsPolicy

由调用线程来处理该任务。一种简单的反馈控制机制,可以减慢提交新任务的速度。

java 复制代码
/**
    * A handler for rejected tasks that runs the rejected task
    * directly in the calling thread of the {@code execute} method,
    * unless the executor has been shut down, in which case the task
    * is discarded.
    */
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    /**
        * Executes task r in the caller's thread, unless the executor
        * has been shut down, in which case the task is discarded.
        */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

DiscardOldestPolicy

丢弃队列中最前面的任务,执行当前被拒绝的任务。

java 复制代码
/**
    * A handler for rejected tasks that discards the oldest unhandled
    * request and then retries {@code execute}, unless the executor
    * is shut down, in which case the task is discarded.
    */
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }

    /**
        * 丢弃原本应该执行的下一个任务,立即执行当前任务
        */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            //丢弃最早的任务
            e.getQueue().poll();
            //执行当前任务
            e.execute(r);
        }
    }
}

线程管理

worker线程

为方便维护线程的状态和管理,ThreadPoolExecutor中每一个线程被封装成Worker类,线程池中实际维护就是一组Worker对象。源码中使用HashSet存储。

java 复制代码
/**
    * Set containing all worker threads in pool. Accessed only when holding mainLock.
    */
private final HashSet<Worker> workers = new HashSet<Worker>();
java 复制代码
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread;
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;
    }
    /**
      * Creates with given first task and thread from ThreadFactory.
      * @param firstTask the first task (null if none)
      */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);
    }
    //...

Class Worker mainly maintains interrupt control state for threads running tasks, along with other minor bookkeeping.

Worker类继承了AQS并实现Runnable,基于AQS实现了一个不可重入互斥锁,以简化获取和释放围绕每个任务执行的锁。之所以不使用ReentrantLock,是因为ReentrantLock是可重入锁,Worker需要通过不可重入特性反应线程的状态。

Worker类维护两个主要属性:

  • thread:处理任务的线程,在构造方法中通过ThreadFactory创建。
  • firstTask:存储第一个任务,可以为null。

firstTask如果为null,则创建线程然后从队列中获取。如果不为null,则是绕过队列,创建线程时直接处理当前任务,一是创建核心线程时,二是队列满时创建非核心。

增加线程

新增线程主要由addWorker方法实现,该方法作用只是新增一个线程,并启动线程。

方法分为两部分,第一步校验是否可以新建,第二步新增Worker。fisrtTask前面已经讲到过,core用于判断工作线程的线程数应当小于corePoolSizemaximumPoolSize,即:wc >= (core ? corePoolSize : maximumPoolSize)

java 复制代码
private boolean addWorker(Runnable firstTask, boolean core) {
    //------1.校验阶段----
    retry:
    for (;;) {
        int c = ctl.get();
        //当前状态
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        //1. rs>=shutdown && rs !=shutdown => rs>shutdown,>=STOP状态拒绝新增线程
        //2. rs==shutdown && firstTask != null,shutdown状态不再接受新任务
        //3. rs==shutdown && firstTask == null && workQueue.isEmpty(),队列已空,则不能新增线程(若非空,则可以增加无任务的线程以处理队列中任务)
        if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                        firstTask == null &&
                        ! workQueue.isEmpty()))
            return false;

        //内层循环
        for (;;) {
            int wc = workerCountOf(c);
            //线程数校验,校验线程数上限,根据core参数校验核心线程or最大线程数
            if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            //worker+1
            if (compareAndIncrementWorkerCount(c))
                //cas添加成功,则结束外层循环,进入后续新建线程阶段
                break retry;
            //cas失败,判断状态是否变更
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                //状态改变,继续循环外层循环
                continue retry;
            //状态未改变,继续内层循环,继续CAS操作
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
    //...
}
java 复制代码
{
    //------2.新建线程阶段----

    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 {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());

                //二次校验,rs<shutdown => running
                if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    //添加到线程池
                    workers.add(w);
                    //记录最大线程数
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                //启动线程,
                //从worker的构造函数可见,t.start()实际上会调用Worker的run(),run()调用runWorker()
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            //线程启动失败,回滚新增的线程
            addWorkerFailed(w);
    }
    return workerStarted;
}

流程图:

线程执行任务

java 复制代码
/** Delegates main run loop to outer runWorker  */
public void run() {
    runWorker(this);
}

Worker类实现Runnable的run方法,调用runWorker。去除掉枝叶,该方法的核心逻辑就是处理任务:task.run()

java 复制代码
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;   //true:执行过程中因异常中断
    try {
        //循环执行getTask从队列中获取任务
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                    //Thread.interrupted()会清除掉中断状态
                    //线程池未停止,确保线程没有中断
                    (Thread.interrupted() &&
                            runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                //线程池停止,确保线程被中断
                wt.interrupt();
            try {
                //hook,执行前
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    //运行任务
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    //hook,执行后
                    afterExecute(task, thrown);
                }
            } finally {
                //执行任务数+1
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        //worker清理回收
        processWorkerExit(w, completedAbruptly);
    }
}

执行流程:

  1. 不断循环调用getTask从队列中获取任务
  2. 判断线程池状态,如果停止则确保线程被中断,否则,确保线程没有中断
  3. 运行任务task.run(),执行预留的钩子,记录已完成的任务数
  4. 主循环中getTask获取不到任务时,执行processWorkerExit回收线程

流程图:

线程回收

runWorker方法中 1. 从任务队列中获取不到任务而退出;2.发生异常,发生这两种情况时调用processWorkerExit对工作线程回收清理。

java 复制代码
final void runWorker(Worker w) {
    completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            //...
            completedAbruptly = false;
        }
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}
java 复制代码
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        //true:线程工作出现异常,worker-1
        decrementWorkerCount();
    //false:runWorker中getTask方法返回null时已-1

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        //记录已完成任务数
        completedTaskCount += w.completedTasks;
        //从worker中删除引用即可,回收工作交给JVM处理
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    //worker数量变动,调用方法处理是否要终止线程池
    tryTerminate();

    int c = ctl.get();
    //1. rs in (running || shutdown),worker异常结束,addWorker
    //2. allowCoreThreadTimeOut = true并且队列非空,至少保留一个线程
    //3. allowCoreThreadTimeOut = false,线程数>=corePoolSize
    //wc不满足上述数量要求,则替换一个非核心线程处理任务
    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);
    }
}

线程回收过程:

  1. 将wc-1并从workers线程集中删除,无引用的对象交给JVM回收处理
  2. wc变动,调用tryTerminate()是否要终止线程池
  3. 调整线程数适应当前状态

至此,工作线程从创建到回收的整个生命流程就完整了。

结尾

本篇从线程池设计思想、理念,再具体讲解线程池是什么、为什么要使用线程池,最后结合源码分析ThreadPoolExecutor三大部分:生命周期管理、线程管理和任务管理。

本篇主要基于源码理解线程池设计和实现原理,下篇重点将讲述线程池在实际使用中需要注意哪些问题,线程池又有哪些不足,如何对线程池进行拓展和分享线上环境线程池出现的问题案例。

其他

  1. Executor中有段关于内存一致性影响描述:

Memory consistency effects: Actions in a thread prior to submitting a Runnable object to an Executor happen-before its execution begins, perhaps in another thread.

  1. ExecutorService

Memory consistency effects: Actions in a thread prior to the submission of a Runnable or Callable task to an ExecutorService happen-before any actions taken by that task, which in turn happen-before the result is retrieved via Future.get().

参考资料

相关推荐
工业互联网专业31 分钟前
毕业设计选题:基于ssm+vue+uniapp的校园水电费管理小程序
vue.js·小程序·uni-app·毕业设计·ssm·源码·课程设计
IT研究室4 天前
大数据毕业设计选题推荐-电影数据分析系统-数据可视化-Hive-Hadoop-Spark
大数据·hive·hadoop·spark·毕业设计·源码·课程设计
IT毕设梦工厂4 天前
大数据毕业设计选题推荐-NBA球员数据分析系统-Python数据可视化-Hive-Hadoop-Spark
大数据·hive·hadoop·spark·毕业设计·源码·课程设计
IT研究室4 天前
大数据毕业设计选题推荐-民族服饰数据分析系统-Python数据可视化-Hive-Hadoop-Spark
大数据·hive·hadoop·spark·毕业设计·源码·课程设计
一 乐5 天前
高校体育场小程序|高校体育场管理系统系统|体育场管理系统小程序设计与实现(源码+数据库+文档)
数据库·小程序·vue·源码·springboot·体育馆小程序
工业互联网专业5 天前
毕业设计选题:基于springboot+vue+uniapp的在线办公小程序
vue.js·spring boot·小程序·uni-app·毕业设计·源码·课程设计
一 乐6 天前
畅阅读小程序|畅阅读系统|基于java的畅阅读系统小程序设计与实现(源码+数据库+文档)
java·小程序·vue·源码·springboot·阅读小程序
一 乐6 天前
助农小程序|助农扶贫系统|基于java的助农扶贫系统小程序设计与实现(源码+数据库+文档)
java·数据库·小程序·vue·源码·助农
一 乐6 天前
订餐点餐|订餐系统基于java的订餐点餐系统小程序设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·源码
Iareges8 天前
PyTorch源码系列(一)——Optimizer源码详解
人工智能·pytorch·python·源码·优化算法·sgd·optimizer