全网最硬核的源码分析之——线程池源码分析

全网最硬核的源码分析之------线程池源码分析

线程池是我们工作中常用的 了解他的运行原理方式和扩展点对大家工作有很大提升 下面会带大家过一下线程池的估计execute方法带大家过一遍线程池执行任务的原理文末还有线程池的扩展点 ,帮助大家更好使用线程池

一 源码分析

关键属性

arduino 复制代码
     /**
     *  控制变量-存放状态和线程数
     */
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

    /**
     * 存放任务的阻塞队列
     */
    private final BlockingQueue<Runnable> workQueue;

    /**
     * 全局锁
     */
    private final ReentrantLock mainLock = new ReentrantLock();

    /**
     * 工作线程集合,存放线程池中所有的(活跃的)工作线程,只有在持有全局锁mainLock的前提下才能访问此集合
     */
    private final HashSet<Worker> workers = new HashSet<>();

    /**
     * awaitTermination方法使用的等待条件变量
     */
    private final Condition termination = mainLock.newCondition();

    /**
     * 峰值线程数 
     */
    private int largestPoolSize;

    /**
     * 已完成任务数 
     */
    private long completedTaskCount;

   

    /**
     * 线程工厂,用于创建新的线程实例
     */
    private volatile ThreadFactory threadFactory;

    /**
     * 拒绝策略
     */
    private volatile RejectedExecutionHandler handler;

    /**
     *  空闲线程等待任务的时间周期,单位是纳秒
     */
    private volatile long keepAliveTime;

    /**
     * 是否允许核心线程超时,如果为true则keepAliveTime对核心线程也生效
     */
    private volatile boolean allowCoreThreadTimeOut;

    /**
     * 核心线程数
     */
    private volatile int corePoolSize;

    /**
     *最大线程数
     */
    private volatile int maximumPoolSize;

构造函数

ini 复制代码
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;
}

可以自定义核心线程数、线程池容量(最大线程数)、空闲线程等待任务周期、任务队列、线程工厂、拒绝策略。下面简单分析一下每个参数的含义和作用:

corePoolSize :int类型,核心线程数量。
maximumPoolSize :int类型,最大线程数量。
keepAliveTime :long类型,线程空闲等待时间,也和工作线程的生命周期有关,下文会分析。
unit :TimeUnit类型,keepAliveTime参数的时间单位,keepAliveTime最终会转化为纳秒。
workQueue :BlockingQueue类型,等待队列或者叫任务队列。
threadFactory :ThreadFactory类型,线程工厂,用于创建工作线程(包括核心线程和非核心线程),默认使用Executors.defaultThreadFactory()作为内建线程工厂实例,一般自定义线程工厂才能更好地跟踪工作线程。
handler :RejectedExecutionHandler类型,线程池的拒绝执行处理器,拒绝策略执行的时机是当阻塞队列已满、没有空闲的线程(包括核心线程和非核心线程)并且继续提交任务。提供了4种内建的拒绝策略实现:

AbortPolicy:直接拒绝策略,也就是不会执行任务,直接抛出RejectedExecutionException,这是默认的拒绝策略。

DiscardPolicy:抛弃策略,也就是直接忽略提交的任务(通俗来说就是空实现)。

DiscardOldestPolicy:抛弃最老任务策略,也就是通过poll()方法取出任务队列队头的任务抛弃,然后执行当前提交的任务。

CallerRunsPolicy:调用者执行策略,也就是当前调用Executor#execute()的线程直接调用任务Runnable#run(),不希望任务丢失会选用这种策略,原来的异步调用意图会退化为同步调用。

状态控制

java 复制代码
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

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;

// 通过ctl值获取运行状态
private static int runStateOf(int c)     { return c & ~COUNT_MASK; }
// 通过ctl值获取工作线程数
private static int workerCountOf(int c)  { return c & COUNT_MASK; }

// 通过运行状态和工作线程数计算ctl的值,或运算
private static int ctlOf(int rs, int wc) { return rs | wc; }

private static boolean runStateLessThan(int c, int s) {
    return c < s;
}

private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}

private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

// CAS操作线程数增加1
private boolean compareAndIncrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect + 1);
}

// CAS操作线程数减少1
private boolean compareAndDecrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect - 1);
}

// 线程数直接减少1
private void decrementWorkerCount() {
    ctl.addAndGet(-1);
}

工作线程上限数量位的长度是COUNT_MASK,值为536870911

RUNNING(-536870912) < SHUTDOWN(0) < STOP(536870912) < TIDYING(1073741824) < TERMINATED(1610612736)

由于运行状态值存放在高3位 可以直接通过十进制值(来比较和判断线程池的状态:

核心方法

execute方法

scss 复制代码
public void execute(Runnable command) {
       // 判断任务对象非空
        if (command == null)
            throw new NullPointerException();
        /*
         * 获取ctl
         */
        int c = ctl.get();
         /*
          * 判断工作线程数是否大于核心线程数 
          */
        if (workerCountOf(c) < corePoolSize) {
            //添加到执行任务队列 成功返回任务
            if (addWorker(command, true))
                return;
           // 这里说明创建核心线程失败,需要更新ctl的临时变量c
            c = ctl.get();
        }
       // 走到这里说明创建新的核心线程失败
       // 判断线程池是否处于运行中状态,同时用非阻塞方法向任务队列放入任务(放入任务失败返回false)
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
           // 如果线程池已不处于 RUNNING 状态,移除已经入队的这个任务,并且执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
           // 走到下面的else if分支,说明有以下的前提:
           // 0、待执行的任务已经成功加入任务队列
           // 1、线程池可能是RUNNING状态
           // 2、传入的任务可能从任务队列中移除失败(移除失败的唯一可能就是任务已经被执行了)
           // 如果当前工作线程数量为0,则创建一个非核心线程并且传入的任务对象为null - 返回
           // 也就是创建的非核心线程不会马上运行,而是等待获取任务队列的任务去执行 
           // 如果前工作线程数量不为0,原来应该是最后的else分支,但是可以什么也不做,因为任务已经成功入队列,总会有合适的时机分配其他空闲线程去执行它
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 如果 workQueue 队列满了,那么进入到这个分支
        // 以 maximumPoolSize 为界创建新的 worker,
        // 如果失败,说明当前线程数已经达到 maximumPoolSize,执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

总结

1 如果当前工作线程总数小于corePoolSize,则直接创建核心线程执行任务

2 如果当前工作线程总数大于等于corePoolSize,判断线程池是否处于运行中状态

同时尝试用非阻塞方法向任务队列放入任务, 二次检查线程池运行状态, 如果当前工作线程数量为0,则创建一个非核心线程并且传入的任务对象为null。

3 如果向任务队列投放任务失败(任务队列已经满了),则会尝试创建非核心线程传入任务实例执行。

4 如果创建非核心线程失败,拒绝执行任务,调用拒绝策略处理任务。

addWorker方法

java 复制代码
// firstTask 任务,core是否为核心线程
// 添加工作线程,如果返回false说明没有新创建工作线程,如果返回true说明创建和启动工作线程成功
private boolean addWorker(Runnable firstTask, boolean core) {
    //标志 可以让嵌套循环直接跳出
    retry:  
    // 注意这是一个死循环 - 最外层循环
    for (int c = ctl.get();;) {
        // 1. 线程池状态至少为SHUTDOWN状态,rs >= SHUTDOWN(0)
        // 2. 线程池状态至少为STOP状态,rs >= STOP(1),或者传入的任务实例firstTask不为null,或者任务队列为空
        // 线程池状态为shutdown状态下,不会再接受新的任务,在此前提下如果状态已经到了STOP、或者传入任务不为空、或者任务队列为空(已经没有积压任务)都不需要添加新的线程
        if (runStateAtLeast(c, SHUTDOWN)
            && (runStateAtLeast(c, STOP)
                || firstTask != null
                || workQueue.isEmpty()))
            return false;
        // 注意这也是一个死循环 - 二层循环
        for (;;) {
            // 这里每一轮循环都会重新获取工作线程数wc
            // 1. 如果传入的core为true,表示将要创建核心线程,通过wc和corePoolSize判断,如果wc >= corePoolSize,则返回false表示创建核心线程失败
            // 1. 如果传入的core为false,表示将要创非建核心线程,通过wc和maximumPoolSize判断,如果wc >= maximumPoolSize,则返回false表示创建非核心线程失败
            if (workerCountOf(c)
                >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                return false;
            // 成功通过CAS更新工作线程数wc,则break到最外层的循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // 走到这里说明了通过CAS更新工作线程数wc失败,这个时候需要重新判断线程池的状态是否由RUNNING已经变为SHUTDOWN
            c = ctl.get();  // Re-read ctl
            // 如果线程池状态已经由RUNNING已经变为SHUTDOWN,则重新跳出到外层循环继续执行
            if (runStateAtLeast(c, SHUTDOWN))
                continue retry;
            // 如果线程池状态依然是RUNNING,CAS更新工作线程数wc失败说明有可能是并发更新导致的失败,则在内层循环重试即可 
            // else CAS failed due to workerCount change; retry inner loop 
        }
    }
    // 标记工作线程是否启动成功
    boolean workerStarted = false;
    // 标记工作线程是否创建成功
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 传入任务实例firstTask创建Worker实例,Worker构造里面会通过线程工厂创建新的Thread对象,所以下面可以直接操作Thread t = w.thread
        // 这一步Worker实例已经创建,但是没有加入工作线程集合或者启动它持有的线程Thread实例
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 这里需要全局加锁,因为会改变一些指标值和非线程安全的集合
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                int c = ctl.get();
                // 这里主要在加锁的前提下判断ThreadFactory创建的线程是否存活或者判断获取锁成功之后线程池状态是否已经更变为SHUTDOWN
                // 1. 如果线程池状态依然为RUNNING,则只需要判断线程实例是否存活,需要添加到工作线程集合和启动新的Worker
                // 2. 如果线程池状态小于STOP,也就是RUNNING或者SHUTDOWN状态下,同时传入的任务实例firstTask为null,则需要添加到工作线程集合和启动新的Worker
                // 对于2,换言之,如果线程池处于SHUTDOWN状态下,同时传入的任务实例firstTask不为null,则不会添加到工作线程集合和启动新的Worker
                // 这一步其实有可能创建了新的Worker实例但是并不启动(临时对象,没有任何强引用),这种Worker有可能成功下一轮GC被收集的垃圾对象
                if (isRunning(c) ||
                    (runStateLessThan(c, STOP) && 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;
                    // 这里更新工作线程是否启动成功标识为true,后面才会调用Thread#start()方法启动真实的线程实例
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 如果成功添加工作线程,则调用Worker内部的线程实例t的Thread#start()方法启动真实的线程实例
            if (workerAdded) {
                t.start();
                // 标记线程启动成功
                workerStarted = true;
            }
        }
    } finally {
        // 线程启动失败,需要从工作线程集合移除对应的Worker
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

总结

该方法主要是将任务添加到线程池工作队列中

返回true为创建成功 返回false为失败

做了 根据入参创建核心 非核心线程

将传入任务包装成Worker

把创建的工作线程实例添加到工作线程集合

如果成功添加工作线程,则调用Worker内部的线程实例t的Thread#start()方法启动真实的线程实例

工作线程内部类Worker

java 复制代码
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
 
  
    private static final long serialVersionUID = 6138294804551838833L;

    // 保存ThreadFactory创建的线程实例,如果ThreadFactory创建线程失败则为null
    final Thread thread;
    // 保存传入的Runnable任务实例
    Runnable firstTask;
    // 记录每个线程完成的任务总数
    volatile long completedTasks;
  
    // 构造函数,传入任务实例firstTask,注意可以为null
    Worker(Runnable firstTask) {
        // 禁止线程中断,直到runWorker()方法执行
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        // 通过ThreadFactory创建线程实例,注意一下Worker实例自身作为Runnable用于创建新的线程实例
        this.thread = getThreadFactory().newThread(this);
    }

    // 委托到外部的runWorker()方法,注意runWorker()方法是线程池的方法,而不是Worker的方法
    public void run() {
        runWorker(this);
    }


    
    //  是否持有独占锁,state值为1的时候表示持有锁,state值为0的时候表示已经释放锁
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }

    // 独占模式下尝试获取资源,这里没有判断传入的变量,直接CAS判断0更新为1是否成功,成功则设置独占线程为当前线程
    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
  
    // 独占模式下尝试是否资源,这里没有判断传入的变量,直接把state设置为0
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
  
    // 加锁
    public void lock()        { acquire(1); }

    // 尝试加锁
    public boolean tryLock()  { return tryAcquire(1); }

    // 解锁
    public void unlock()      { release(1); }

    // 是否锁定
    public boolean isLocked() { return isHeldExclusively(); }
  
    // 启动后进行线程中断,注意这里会判断线程实例的中断标志位是否为false,只有中断标志位为false才会中断
    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

总结

Worker 是线程池存放任务的对象 主要包装需要执行的线程和任务

可以通过completedTasks 获取单个线程的完成任务

runWorker

scss 复制代码
final void runWorker(Worker w) {
    // 获取当前线程,Worker持有的线程实例
    Thread wt = Thread.currentThread();
    // 获取Worker中持有的初始化时传入的任务对象
    Runnable task = w.firstTask;
    // 设置Worker中持有的初始化时传入的任务对象为null
    w.firstTask = null;
    // 由于Worker初始化时AQS中state设置为-1,这里要先做一次解锁把state更新为0,允许线程中断
    w.unlock(); 
    // 记录线程是否因为用户异常终结,默认是true
    boolean completedAbruptly = true;
    try {
        // 初始化任务对象不为null,或者从任务队列获取任务不为空(从任务队列获取到的任务会更新到临时变量task中)
        // getTask()由于使用了阻塞队列,这个while循环如果命中后半段会处于阻塞或者超时阻塞状态,getTask()返回为null会导致线程跳出死循环使线程终结
        while (task != null || (task = getTask()) != null) {
            // Worker加锁,本质是AQS获取资源并且尝试CAS更新state由0更变为1
            w.lock();
   
            // 如果线程池正在停止(也就是由RUNNING或者SHUTDOWN状态向STOP状态变更),那么要确保当前工作线程是中断状态
            // 否则,要保证当前线程不是中断状态
            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临时变量,这个很重要,否则while会死循环执行同一个task
                task = null;
                // 累加Worker完成的任务数
                w.completedTasks++;
                // Worker解锁,本质是AQS释放资源,设置state为0
                w.unlock();
            }
        }
        // 走到这里说明某一次getTask()返回为null,线程正常退出
        completedAbruptly = false;
    } finally {
       // 1. 说明 getTask 返回 null,也就是说,队列中已经没有任务需要执行了,执行关闭
       // 2. 任务执行过程中发生了异常
       // 第一种情况,已经在代码处理了将 workCount 减 1,这个在 getTask 方法分析中会说
       // 第二种情况,workCount 没有进行处理,所以需要在 processWorkerExit 中处理
        processWorkerExit(w, completedAbruptly);
    }
}

总结

运行当前传入的worker 提供了beforeExecute和afterExecute两个钩子函数可以对执行任务进行增强

getTask

java 复制代码
private Runnable getTask() {
    // 记录上一次从队列中拉取的时候是否超时
    boolean timedOut = false; // Did the last poll() time out?
    for (;;) {
        int c = ctl.get();

  
        // 1:如果线程池状态至少为SHUTDOWN,也就是rs >= SHUTDOWN(0),则需要判断两种情况(或逻辑):
        // 2. 线程池状态至少为STOP(1),也就是线程池正在停止,一般是调用了shutdownNow()方法
        // 3. 任务队列为空
        // 如果在线程池至少为SHUTDOWN状态并且满足上面两个条件之一,则工作线程数wc减去1,然后直接返回null
        if (runStateAtLeast(c, SHUTDOWN)
            && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        // 跑到这里说明线程池还处于RUNNING状态,重新获取一次工作线程数
        int wc = workerCountOf(c);
  
        // timed临时变量用于线程超时控制,决定是否需要通过poll()此带超时的非阻塞方法进行任务队列的任务拉取
        // 1.allowCoreThreadTimeOut默认值为false,如果设置为true,则允许核心线程也能通过poll()方法从任务队列中拉取任务
        // 2.工作线程数大于核心线程数的时候,说明线程池中创建了额外的非核心线程,这些非核心线程一定是通过poll()方法从任务队列中拉取任务
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
  
        // 1.wc > maximumPoolSize说明当前的工作线程总数大于maximumPoolSize,说明了通过setMaximumPoolSize()方法减少了线程池容量
        // 或 2.timed && timedOut说明了线程命中了超时控制并且上一轮循环通过poll()方法从任务队列中拉取任务为null
        // 并且 3. 工作线程总数大于1或者任务队列为空,则通过CAS把线程数减去1,同时返回null,
        // CAS把线程数减去1失败会进入下一轮循环做 重试
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            // 如果timed为true,通过poll()方法做超时拉取,keepAliveTime时间内没有等待到有效的任务,则返回null
            // 如果timed为false,通过take()做阻塞拉取,会阻塞到有下一个有效的任务时候再返回(一般不会是null)
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            // 这里很重要,只有非null时候才返回,null的情况下会进入下一轮循环
            if (r != null)
                return r;
            // 跑到这里说明上一次从任务队列中获取到的任务为null,一般是workQueue.poll()方法超时返回null
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

总结

getTask 从work队列中通过阻塞拉取或者超时拉取拿到执行的任务

非核心线程调用 poll 方法,核心线程调用 take 方法,take方法是阻塞的,获取不到数据会一直等着。

需要注意的点可以通过allowCoreThreadTimeOut控制核心线程是否允许超时

processWorkerExit

scss 复制代码
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 因为抛出用户异常导致线程终结,直接使工作线程数减1即可
    // 如果没有任何异常抛出的情况下是通过getTask()返回null引导线程正常跳出runWorker()方法的while死循环从而正常终结,这种情况下,在getTask()中已经把线程数减1
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 全局的已完成任务记录数加上此将要终结的Worker中的已完成任务数
        completedTaskCount += w.completedTasks;
        // 工作线程集合中移除此将要终结的Worker
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
   
    // 用于根据当前线程池的状态判断是否需要进行线程池terminate处理
    tryTerminate();

    int c = ctl.get();
    // 如果线程池的状态小于STOP,也就是处于RUNNING或者SHUTDOWN状态的前提下:
    // 1.如果线程不是由于抛出用户异常终结,如果允许核心线程超时,则保持线程池中至少存在一个工作线程
    // 2.如果线程由于抛出用户异常终结,或者当前工作线程数,那么直接添加一个新的非核心线程
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            // 如果允许核心线程超时,最小值为0,否则为corePoolSize
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 如果最小值为0,同时任务队列不空,则更新最小值为1
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            // 工作线程数大于等于最小值,直接返回不新增非核心线程
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}

总结

processWorkerExit

处理因为异常导致的线程终结和将当前线程处理的任务数复制给总线程池处理任务数

注意就算配置了核心线程允许超时也会留一个线程

tryTerminate

scss 复制代码
final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        // 判断线程池的状态,如果是下面三种情况下的任意一种则直接返回:
        // 1.线程池处于RUNNING状态
        // 2.线程池至少为TIDYING状态,也就是TIDYING或者TERMINATED状态,走到了下面的步骤,线程池即将终结
        // 3.线程池至少为STOP状态并且任务队列不为空
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateLessThan(c, STOP) && ! workQueue.isEmpty()))
            return;
        // 工作线程数不为0,则中断工作线程集合中的第一个空闲的工作线程
        if (workerCountOf(c) != 0) { // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // CAS设置线程池状态为TIDYING,如果设置成功则执行钩子方法terminated()
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated();
                } finally {
                    // 最后更新线程池状态为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    // 唤醒阻塞在termination条件的所有线程
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
    }
}

总结

尝试将线程状态改为TIDYING 修改状态为TIDYING后执行terminated()方法,最后修改状态为TERMINATED,标志着线程真正消亡。

shutdown

scss 复制代码
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 权限校验,安全策略相关判断
        checkShutdownAccess();
        // 设置SHUTDOWN状态
        advanceRunState(SHUTDOWN);
        // 中断所有的空闲的工作线程
        interruptIdleWorkers();
        // 钩子方法
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
   // 调用tryTerminate方法,使状态更变为TIDYING,执行钩子方法terminated()后,最终状态更新为TERMINATED
   tryTerminate();
}

// 升提状态
private void advanceRunState(int targetState) {
    // assert targetState == SHUTDOWN || targetState == STOP;
    for (;;) {
        int c = ctl.get();
        // 线程池状态至少为targetState或者CAS设置状态为targetState则跳出循环
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

// 中断所有的空闲的工作线程
private void interruptIdleWorkers() {
    interruptIdleWorkers(false);
}

shutdown 关闭线程池,已提交的任务继续执行,不接受继续提交新任务

shutdownNow

scss 复制代码
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 权限校验
        checkShutdownAccess();
        // 设置STOP状态
        advanceRunState(STOP);
        // 中断所有的工作线程
        interruptWorkers();
        // 清空工作队列并且取出所有的未执行的任务
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
     // 调用tryTerminate方法,使状态更变为TIDYING,执行钩子方法terminated()后,最终状态更新为TERMINATED
    tryTerminate();
    return tasks;
}

// 遍历所有的工作线程,如果state > 0(启动状态)则进行中断
private void interruptWorkers() {
    // assert mainLock.isHeldByCurrentThread();
    for (Worker w : workers)
        w.interruptIfStarted();
}

shutdownNow 关闭线程池,不管任务是不是再执行

小结

mainLock

方法 主要作用
tryTerminate 保证状态TIDYING -> TERMINATED,钩子方法terminated()回调和条件变量唤醒
interruptIdleWorkers 保护工作线程中断的串行化
addWorker 保护工作线程集合避免并发增加工作线程、和计数数据变更
processWorkerExit 统计数据变更
shutdown、shutdownNow 保护以下方法串行化// 权限校验,安全策略相关判断checkShutdownAccess();// 设置SHUTDOWN状态advanceRunState(SHUTDOWN);// 中断所有的空闲的工作线程interruptIdleWorkers();// 钩子方法onShutdown();
getPoolSize、getActiveCount、getLargestPoolSize、getTaskCount和getCompletedTaskCount 统计数据读取,统计数据一般源于Worker集合的属性统计

线程池的扩展方法和监控方法

钩子函数

  • beforeExecute(Thread t, Runnable r):任务对象Runnable#run()执行之前触发回调。
  • afterExecute(Runnable r, Throwable t):任务对象Runnable#run()执行之后(包括异常完成情况和正常完成情况)触发回调。
  • terminated():线程池关闭的时候,状态更变为TIDYING成功之后会回调此方法,执行此方法完毕后,线程池状态会更新为TERMINATED。
  • onShutdown():shutdown()方法执行时候会回调此方法

核心线程相关:

  • getCorePoolSize():获取核心线程数。
  • setCorePoolSize():重新设置线程池的核心线程数。
  • prestartCoreThread():预启动一个核心线程,当且仅当工作线程数量小于核心线程数量。
  • prestartAllCoreThreads():预启动所有核心线程。

线程池容量相关:

  • getMaximumPoolSize():获取线程池容量。
  • setMaximumPoolSize():重新设置线程池的最大容量。

线程存活周期相关:

  • setKeepAliveTime():设置空闲工作线程的存活周期。
  • getKeepAliveTime():获取空闲工作线程的存活周期。

监控统计相关方法:

  • getTaskCount():获取所有已经被执行的任务总数的近似值。
  • getCompletedTaskCount():获取所有已经执行完成的任务总数的近似值。
  • getLargestPoolSize():获取线程池的峰值线程数(最大池容量)。
  • getActiveCount():获取所有活跃线程总数(正在执行任务的工作线程)的近似值。
  • getPoolSize():获取工作线程集合的容量(当前线程池中的总工作线程数)。

务队列操作相关方法:

  • purge():移除任务队列中所有是Future类型并且已经处于Cancelled状态的任务。
  • remove():从任务队列中移除指定的任务。
  • BlockingQueue getQueue():获取任务队列的引用。
相关推荐
NiNg_1_2342 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk4 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*4 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue4 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man4 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer086 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml47 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠8 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#