Java基础学习之深入理解ThreadPoolExectuor

0.写在最前面

最近学习ThreadPoolExecutor这个类,然后也查阅了一些博客,这里只是自己摘录的内容,非原创,请大家支持原创,结尾给出参考的内容。

1. 什么是线程池?

线程池是一种基于池化思想管理线程的工具,经常出现在多线程服务器中。
线程过多会带来额外的开销,其中包括创建线程、销毁线程等等,这些额外的开销会降低计算机的性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。

2.线程池解决了什么问题?

线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能确定在某些时刻,有多少任务在执行,多少资源需要投入。这种不确定性会带来以下问题:

  • 频繁申请、销毁资源,将带来额外的消耗,可能会非常巨大。
  • 可以提高响应速度。当任务到达时,无序等待线程创建。
  • 提高线程的可管理性。

3. Executor

JAVA 中线程池的核心实现类是ThreadPoolExecutor,其UML图如下
UML
ThreadPoolExecutor实现的顶层接口是Executor,只包含了一个方法execute()。Executor提供了一种设计思想:将任务提交和任务执行过程进行解耦。用户无需关注如何创建线程,也无需关注如何调度线程执行任务。用户只需要提供Runnable对象,将任务的运行逻辑提交到Executor中,有Executor框架来完成线程的调配和任务的执行。
ExecutorService接口增加了一些能力,提供了更多的方法:
在这里插入图片描述
ExecutorService的方法主要分为两类:

  • 管理线程池的方法,如关闭线程池。
  • 扩充执行任务的能力,如可以为一个或多个任务生成异步Future的方法。

在这里插入图片描述
AbstractExecutorService则是上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。

TheadPoolExecutor则是更复杂的具体实现。下面详细学习。

4.ThreadPoolExecutor

首先看这个类的构造函数(7个参数):

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
 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;
    }

这个包含7个参数的构造函数,参数分别是

  • int corePoolSize 核心线程数
  • int maximumPoolSize 最大线程数量
  • long keepAliveTime 存活的时长
  • TimeUnit
  • BlockingQueue workQueue 工作队列
  • ThreadFactory threadFactory
  • RejectedExecutionHandler handler 拒绝策略

这里说一下存活时长keepAliveTime,源码中的注释是:

when the number of threads is greater than  the core, this is the maximum time that excess idle threads  will wait for new tasks before terminating.

当线程的数量大于核心线程数量时,这是多余的空闲线程在终止前等待新任务的最长时间。即超过这个时间,就会销毁多余的线程。

还有workQueue的注释:

the queue to use for holding tasks before they are executed.  This queue will hold only the {@code Runnable}  tasks submitted by the {@code execute} method.

这个队列用来存放将要被执行的任务。队列里只会存放execute方法提交的任务。

handler的注释是

the handler to use when execution is blocked because the thread bounds and queue capacities are reached

由于达到线程边界和队列容量而阻塞执行时要使用的处理程序。即当队列满的时候将会采取的拒绝策略。

一脸懵的应该不止我一个,那画个图好了:
在这里插入图片描述
可以看到,ThreadPoolExecutor在内部构建了一个生产者-消费者模型,将任务和线程两者进行解耦,并不直接关联。任务管理的部分作为生产者,决定进task是直接提交给线程池执行,还是提交进阻塞队列。线程池部分作为消费者,根据任务请求分配线程,当执行完一个任务后继续获取新的 任务去执行,直到没有任务了,超过coresize的线程会被回收。

5.线程的状态

在Thread类的源码中,我们可以看到有一个枚举类State:

/**
     * A thread state.  A thread can be in one of the following states:
     * <ul>
     * <li>{@link #NEW}<br>
     *     A thread that has not yet started is in this state.
     *     </li>
     * <li>{@link #RUNNABLE}<br>
     *     A thread executing in the Java virtual machine is in this state.
     *     </li>
     * <li>{@link #BLOCKED}<br>
     *     A thread that is blocked waiting for a monitor lock
     *     is in this state.
     *     </li>
     * <li>{@link #WAITING}<br>
     *     A thread that is waiting indefinitely for another thread to
     *     perform a particular action is in this state.
     *     </li>
     * <li>{@link #TIMED_WAITING}<br>
     *     A thread that is waiting for another thread to perform an action
     *     for up to a specified waiting time is in this state.
     *     </li>
     * <li>{@link #TERMINATED}<br>
     *     A thread that has exited is in this state.
     *     </li>
     * </ul>
     *
     * <p>
     * A thread can be in only one state at a given point in time.
     * These states are virtual machine states which do not reflect
     * any operating system thread states.
     *
     * @since   1.5
     * @see #getState
     */
    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

共有6个状态,即

序号状态备注
1NEW新创建了一个线程,但是还没有调用start方法
2RUNNABLE运行状态的线程,可以是正在运行的线程,也可以是正在等待其他资源的状态
3BLOCKED运行中的线程,但是处于阻塞状态,比方说调用了Object的wait方法后,等待进入同步方法或者同步块
4WAITING等待状态,如调用了wait方法或者join方法或者park方法后的状态,在等待notify或者notifyALL方法来进行唤醒
5TIMED_WATING线程等待一定时间,主要是调用了给定时间的等待方法,包括sleep,wait,join和park,都指定了等待的时间
6TERMINATED线程执行完成后,将终止线程。

那么这6个状态是怎么切换的呢?
线程状态转换图
当我们继承了Thread类或者实现了Callable或者Runnable接口,创建了一个线程,但是线程还没有调用的时候,就是NEW的状态。
当线程调用了start()方法后,线程就进入了Runnable状态。但是这个状态又分为两个阶段,第一阶段是Ready状态,即已经可以运行了,但是可能其他资源还没有装备好,无法进入running状态,比如CPU执行时间片没分到。
当线程的run方法执行结束或者main方法结束,线程就会进入到Terminated状态。
当处于Runnable状态的线程,调用了指定时间的方法,即

  • Thread.sleep(sleeptime)
  • Object.wait(timeout)
  • Thread.join(timeout)
  • LockSupport.parkNanos(timeout)
  • LockSupport.parkUntil(timeout)
    线程就会进入到Timed waiting状态。等待时间结束后,sleep方法会继续回到Runnable状态,因为它没有释放资源。
    而wait方法在时间到了或者被唤醒后进入Blocked状态,开始新一轮的资源争夺,等待加锁的过程。
    waiting 状态是由于线程调用了以下方法
  • Object.wait()
  • Thread.join()
  • LockSupport.park()
    他们需要等待唤醒进入Blocked状态参与新一轮资源争夺的。

6.线程池是如何维护自生状态的?

线程池运行的状态,并不是用户显式设置的,而是伴随着线程池的运行,由内部来维护。线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量 (workerCount)。在具体实现中,线程池将运行状态(runState)、线程数量 (workerCount)两个关键参数的维护放在了一起,如下代码所示:

/*
The main pool control state, ctl, is an atomic integer packing
     * two conceptual fields
     *   workerCount, indicating the effective number of threads
     *   runState,    indicating whether running, shutting down etc
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ctl这个AtomicInteger类型,是对线程池的运行状态和线程池中有效线程的数量进行控制的一个字段, 它同时包含两部分的信息:线程池的运行状态 (runState) 和线程池内有效线程的数量 (workerCount),高3位保存runState,低29位保存workerCount,两个变量之间互不干扰。用一个变量去存储两个值,可避免在做相关决策时,出现不一致的情况,不必为了维护两者的一致,而占用锁资源。通过阅读线程池源代码也可以发现,经常出现要同时判断线程池运行状态和线程数量的情况。线程池也提供了若干方法去供用户获得线程池当前的运行状态、线程个数。这里都使用的是位运算的方式,相比于基本运算,速度也会快很多。
关于内部封装的获取生命周期状态、获取线程池线程数量的计算方法如以下代码所示:

// Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

ThreadPoolExecutor的运行状态又5种:

运行状态源码注释备注
RUNNINGAccept new tasks and process queued tasks接受新的任务,并且也能处理阻塞队列中的任务
SHUTDOWN:Don’t accept new tasks, but process queued tasks关闭状态,不接受新的任务,但是继续处理队列中的任务
STOPDon’t accept new tasks, don’t process queued tasks, and interrupt in-progress tasks不接受新的任务,不处理队列中的任务,并且打断进行中的任务
TIDYINGAll tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method所有任务结束,workerCount为0,TIDYING将run Terminated方法
TERMINATEDterminated() has completedterminated()方法执行完成后

ThreadPoolExecutor的状态转换如图:
在这里插入图片描述

7.任务调度机制

7.1 任务调度

首先,所有的任务调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下里的执行流程,是直接申请线程执行还是缓冲队列执行,又或者是直接拒绝该任务。画个流程图好了
在这里插入图片描述
1.首先查看线程池运行状态,如果不是running状态,则直接拒绝。
2.如果workCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
3.如果workCount >= corePoolSize, 且阻塞队列未满,则将任务添加到该阻塞队列中。
4.如果workCount >= corePoolSize 且workCount < maximumPoolSize, 且队列也满了,则创建一个新的线程来执行新提交的任务。
5.如果workCount >= maximumPoolSize,并且阻塞队列已满了,则根据拒绝策略来处理该任务。

7.2 任务缓冲

任务缓冲模块是线程池能够管理任务的核心部分。线程池的本质是对任务和线程的管理,而做到这一点最关键的思想就是将任务和线程两者解耦,不让两者直接关联,才可以做后续的分配工作。线程池中是以生产者消费者模式,通过一个阻塞队列来实现的。阻塞队列缓存任务,工作线程从阻塞队列中获取任务。

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
使用不同的队列可以实现不一样的任务存取策略。

名称描述
ArrayBlockingQueue一个用数组实现的有界阻塞队列,此队列按照先进先出的原则对元素进行排序。支持公平锁和非公平锁。
LinkedBlockingQueue一个由链表组成的有界队列,FIFO。默认长度为Integer.MAX_VALUE,因此默认创建的该队列有容量危险。
PriorityBlockingQueue一个支持线程优先级排序的无界队列,默认自然排序,也可以通过实现compareTo()方法来指定元素排序规则,但是不能保证相同优先级元素的顺序。
DelayQueue一个实现了PriorityBlockingQueue来实现延迟获取的无界队列,在创建元素时,可以指定多久才能从队列中获取当前元素。只有delay时间到了才可以获取
synchronousQueue一个不存贮元素的队列,每一个put操作都必须等待take操作,否则不能添加元素。支持公平锁和非公平锁。synchronousQueue的一个使用场景是在线程池里。Executors.newCachedThreadPool()就使用了synchronousQueue。这个线程池根据需要创建新的线程,如果有闲置就会重复使用。线程空闲了60s后就会被回收。
LinkedTransferQueue一个由链表结构组成的无界阻塞队列,相对于其他队列,LInkedTransferQueue队列多了transfer和tryTransfer方法
LinkedBlockingDeque一个由链表结构组成的双向阻塞队列,队列头部和尾部都可以进行元素添加和移除,多线程并发时,可以将锁竞争最多降到一半

7.3 任务申请

由上文的任务分配部分可知,任务的执行有两种可能:一种是任务直接由新创建的线程执行。另一种是线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行。第一种情况仅出现在线程初始创建的时候,第二种是线程获取任务绝大多数的情况。

线程需要从任务缓存模块中不断地取任务执行,帮助线程从阻塞队列中获取任务,实现线程管理模块和任务管理模块之间的通信。这部分策略由getTask方法实现,其执行流程如下图所示:
在这里插入图片描述
getTask这部分进行了多次判断,为的是控制线程的数量,使其符合线程池的状态。如果线程池现在不应该持有那么多线程,则会返回null值。工作线程Worker会不断接收新任务去执行,而当工作线程Worker接收不到任务的时候,就会开始被回收。

7.4 任务拒绝

任务拒绝模块是线程池的保护部分,线程池有一个最大的容量,当线程池的任务缓存队列已满,并且线程池中的线程数目达到maximumPoolSize时,就需要拒绝掉该任务,采取任务拒绝策略,保护线程池。

拒绝策略是一个接口,其设计如下:

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

JDK提供了4种拒绝策略,即4个静态类:

名称描述
ThreadPoolExecutor.AbortPolicy丢弃任务并且抛出RejectedExecutionException异常。这是线程池默认的拒绝策略,在任务不能提交的时候,抛出异常,及时反馈程序运行状态。如果是比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承受更大的并发量的时候,能够及时通过异常发现。
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常。使用此策略,可能会使我们无法发现系统的异常状态。建议是一些无关紧要的业务采用此策略。
ThreadPoolExecutor.DiscardOldestPolicy丢弃队列最前面的任务,然后重新提交被拒绝的任务。
ThreadPoolExecutor.CallerRunsPolicy由调用线程(提交任务的线程)处理该任务。这种情况是需要让所有任务都执行完毕,那么就适合大量计算的任务类型去执行,多线程仅仅是增大吞吐量的手段,最终必须要让每个任务都执行完毕。

8.Worker线程管理

8.1 Worker线程

线程池为了掌握线程的状态并维护线程的生命周期,设计了线程池内的工作线程Worker。

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable {
	 final Thread thread;//Worker持有的线程
    Runnable firstTask;//初始化的任务,可以为null
}

Worker这个工作线程,实现了Runnable接口,并持有一个线程thread,一个初始化的任务firstTask。thread是在调用构造方法时通过ThreadFactory来创建的线程,可以用来执行任务;firstTask用它来保存传入的第一个任务,这个任务可以有也可以为null。如果这个值是非空的,那么线程就会在启动初期立即执行这个任务,也就对应核心线程创建时的情况;如果这个值是null,那么就需要创建一个线程去执行任务列表(workQueue)中的任务,也就是非核心线程的创建。
在这里插入图片描述
线程池需要管理线程的生命周期,需要在线程长时间不运行的时候进行回收。线程池使用一张Hash表去持有线程的引用,这样可以通过添加引用、移除引用这样的操作来控制线程的生命周期。这个时候重要的就是如何判断线程是否在运行。

​Worker是通过继承AQS,使用AQS来实现独占锁这个功能。没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态。

1.lock方法一旦获取了独占锁,表示当前线程正在执行任务中。 2.如果正在执行任务,则不应该中断线程。 3.如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断。 4.线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;如果线程是空闲状态则可以安全回收。
在这里插入图片描述

8.2 worker线程的增加

增加线程是通过线程池中的addWorker方法,该方法的功能就是增加一个线程,该方法不考虑线程池是在哪个阶段增加的该线程,这个分配线程的策略是在上个步骤完成的,该步骤仅仅完成增加线程,并使它运行,最后返回是否成功这个结果。

/**
     * Checks if a new worker can be added with respect to current
     * pool state and the given bound (either core or maximum). If so,
     * the worker count is adjusted accordingly, and, if possible, a
     * new worker is created and started, running firstTask as its
     * first task. This method returns false if the pool is stopped or
     * eligible to shut down. It also returns false if the thread
     * factory fails to create a thread when asked.  If the thread
     * creation fails, either due to the thread factory returning
     * null, or due to an exception (typically OutOfMemoryError in
     * Thread.start()), we roll back cleanly.
     *
     * @param firstTask the task the new thread should run first (or
     * null if none). Workers are created with an initial first task
     * (in method execute()) to bypass queuing when there are fewer
     * than corePoolSize threads (in which case we always start one),
     * or when the queue is full (in which case we must bypass queue).
     * Initially idle threads are usually created via
     * prestartCoreThread or to replace other dying workers.
     *
     * @param core if true use corePoolSize as bound, else
     * maximumPoolSize. (A boolean indicator is used here rather than a
     * value to ensure reads of fresh values after checking other pool
     * state).
     * @return true if successful
     */
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

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

                    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) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

addWorker方法有两个参数:firstTask、core。firstTask参数用于指定新增的线程执行的第一个任务,该参数可以为空;core参数为true表示在新增线程时会判断当前活动线程数是否少于corePoolSize,false表示新增线程前需要判断当前活动线程数是否少于maximumPoolSize。
在这里插入图片描述

8.3 worker线程回收

线程池中线程的销毁依赖JVM自动的回收,线程池做的工作是根据当前线程池的状态维护一定数量的线程引用,防止这部分线程被JVM回收,当线程池决定哪些线程需要回收时,只需要将其引用消除即可。Worker被创建出来后,就会不断地进行轮询,然后获取任务去执行,核心线程可以无限等待获取任务,非核心线程要限时获取任务。当Worker无法获取到任务,也就是获取的任务为空时,循环会结束,Worker会主动消除自身在线程池内的引用。

try {
  while (task != null || (task = getTask()) != null) {
    //执行任务
  }
} finally {
  processWorkerExit(w, completedAbruptly);//获取不到任务时,主动回收自己
}

线程回收的工作是在processWorkerExit方法中进行的。

/**
     * Performs cleanup and bookkeeping for a dying worker. Called
     * only from worker threads. Unless completedAbruptly is set,
     * assumes that workerCount has already been adjusted to account
     * for exit.  This method removes thread from worker set, and
     * possibly terminates the pool or replaces the worker if either
     * it exited due to user task exception or if fewer than
     * corePoolSize workers are running or queue is non-empty but
     * there are no workers.
     *
     * @param w the worker
     * @param completedAbruptly if the worker died due to user exception
     */
    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;
            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);
        }
    }

8.4 Worker线程执行任务

在Worker类中的run方法调用了runWorker方法来执行任务,runWorker方法的执行过程如下:

1.while循环不断地通过getTask()方法获取任务。
2.getTask()方法从阻塞队列中取任务。
3.如果线程池正在停止,那么要保证当前线程是中断状态,否则要保证当前线程不是中断状态。
4.执行任务。
5.如果getTask结果为null则跳出循环,执行processWorkerExit()方法,销毁线程。
在这里插入图片描述

9.JDK提供的四个线程池

JDK提供四个线程池的实现,分别是:

  • newFixedThreadPool() —> 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newSingleThreadExecutor() —> 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
  • newCachedThreadPool() --> 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newScheduledThreadPool() —>创建一个周期线程池,支持定时及周期性任务执行。

10.感谢大佬

  1. Java线程池实现原理及其在美团业务中的实践
  2. 线程的几种状态转换
  3. Java线程状态转换
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值