ThreadPoolExecutor任务提交

本文详细分析了Java ThreadPoolExecutor的执行流程,包括线程池的五种状态,任务提交的内部原理,如execute()方法的工作逻辑,线程池如何添加worker线程,worker线程的执行任务过程,以及worker线程退出时的处理。通过深入理解这些概念,有助于更好地掌握Java线程池的使用和管理。
摘要由CSDN通过智能技术生成

execute()是 java.util.concurrent.Executor接口中唯一的方法,JDK注释中的描述是“在未来的某一时刻执行命令command”,即向线程池中提交任务,在未来某个时刻执行,提交的任务必须实现Runnable接口,该提交方式不能获取返回值。下面是对execute()方法内部原理的分析,分析前先简单介绍线程池有哪些状态,在一系列执行过程中涉及线程池状态相关的判断。以下分析基于JDK 1.7

线程池的执行流程在这里插入图片描述

在这里插入图片描述
1、如果线程池中的线程数量少于corePoolSize,就创建新的线程来执行新添加的任务
2、如果线程池中的线程数量大于等于corePoolSize,但队列workQueue未满,则将新添加的任务放到workQueue中
3、如果线程池中的线程数量大于等于corePoolSize,且队列workQueue已满,但线程池中的线程数量小于maximumPoolSize,则会创建新的线程来处理被添加的任务
4、如果线程池中的线程数量等于了maximumPoolSize,就用RejectedExecutionHandler来执行拒绝策略

线程池状态:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
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;

// 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; }

其中ctl这个AtomicInteger的功能很强大,其高3位用于维护线程池运行状态,低29位维护线程池中线程数量
1**、RUNNING**:-1<<COUNT_BITS,即高3位为1,低29位为0,该状态的线程池会接收新任务,也会处理在阻塞队列中等待处理的任务
2、SHUTDOWN:0<<COUNT_BITS,即高3位为0,低29位为0,该状态的线程池不会再接收新任务,但还会处理已经提交到阻塞队列中等待处理的任务
3、STOP:1<<COUNT_BITS,即高3位为001,低29位为0,该状态的线程池不会再接收新任务,不会处理在阻塞队列中等待的任务,而且还会中断正在运行的任务
4、TIDYING:2<<COUNT_BITS,即高3位为010,低29位为0,所有任务都被终止了,workerCount为0,为此状态时还将调用terminated()方法
5**、TERMINATED**:3<<COUNT_BITS,即高3位为100,低29位为0,terminated()方法调用完成后变成此状态
这些状态均由int型表示,大小关系为 RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED,这个顺序基本上也是遵循线程池从 运行 到 终止这个过程。
runStateOf(int c) 方法:c & 高3位为1,低29位为0的~CAPACITY,用于获取高3位保存的线程池状态
**workerCountOf(int c)**方法:c & 高3位为0,低29位为1的CAPACITY,用于获取低29位的线程数量
**ctlOf(int rs, int wc)**方法:参数rs表示runState,参数wc表示workerCount,即根据runState和workerCount打包合并成ctl

任务提交内部原理

execute():提交任务

在这里插入图片描述

/**
 * 在未来的某个时刻执行给定的任务。
 * 这个任务用一个新线程执行,或者用一个线程池中已经存在的线程执行
 *
 * 如果任务无法被提交执行,要么是因为这个Executor已经被shutdown关闭,
 * 要么是已经达到其容量上限,任务会被当前的RejectedExecutionHandler处理
 */
public void execute(Runnable command) {
   
    if (command == null)
        throw new NullPointerException();
     
    /*
     * 任务分三步:
     * 1、如果运行的线程少于corePoolSize,尝试开启一个新线程去运行command,
     * command作为这个线程的第一个任务
     *
     * 2、如果任务成功放入队列,我们仍需要一个双重校验去确认是否应该新建一个线程
     *(因为可能存在有些线程在我们上次检查后死了) 或者 从我们进入这个方法后,pool被关闭了
     * 所以我们需要再次检查state,如果线程池停止了需要回滚入队列,
     * 如果池中没有线程了,新开启一个线程
     * 
     * 3、如果无法将任务入队列(可能队列满了),需要新开区一个线程(自己:往maxPoolSize发展)
     * 如果失败了,说明线程池shutdown 或者 饱和了,所以我们拒绝任务
     */
    int c = ctl.get();
     
    /**
     * 1、如果当前线程数少于corePoolSize
     *(可能是由于addWorker()操作已经包含对线程池状态的判断,如此处没加,而入workQueue前加了)
     */
    if (workerCountOf(c) < corePoolSize) {
   
        //addWorker()成功,返回
        if (addWorker(command, true))
            return;
         
        /**
         * 没有成功addWorker(),再次获取c(凡是需要再次用ctl做判断时,都会再次调用ctl.get())
         * 失败的原因可能是:
         * 1、线程池已经shutdown,shutdown的线程池不再接收新任务
         * 2、workerCountOf(c) < corePoolSize 判断后,由于并发,
         * 别的线程先创建了worker线程,导致workerCount>=corePoolSize
         */
        c = ctl.get();
    }
     
    /**
     * 2、如果线程池RUNNING状态,且入队列成功
     */
    if (isRunning(c) && workQueue.offer(command)) {
   
        int recheck = ctl.get();//再次校验位
         
        /**
         * 再次校验放入workerQueue中的任务是否能被执行
         * 1、如果线程池不是运行状态了,应该拒绝添加新任务,从workQueue中删除任务
         * 2、如果线程池是运行状态,或者从workQueue中删除任务失败
         *(刚好有一个线程执行完毕,并消耗了这个任务),确保还有线程执行任务(只要有一个就够了)
         */
        //如果再次校验过程中,线程池不是RUNNING状态,
        //并且remove(command)--workQueue.remove()成功,拒绝当前command
        if (! isRunning(recheck) && remove(command))
            reject(command);
        //如果当前worker数量为0,通过addWorker(null, false)创建一个线程,其任务为null
        //为什么只检查运行的worker数量是不是0呢?? 为什么不和corePoolSize比较呢??
        //只保证有一个worker线程可以从queue中获取任务执行就行了??
        //因为只要还有活动的worker线程,就可以消费workerQueue中的任务
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);  
        //第一个参数为null,说明只为新建一个worker线程,没有指定firstTask
        //第二个参数为true代表占用corePoolSize,false占用maxPoolSize
    }
    /**
     * 3、如果线程池不是running状态 或者 无法入队列
     * 尝试开启新线程,扩容至maxPoolSize,如果addWork(command, false)失败了,
     * 拒绝当前command
     */
    else if (!addWorker(command, false))
        reject(command);
}

execute(Runnable command)
参数:
command 提交执行的任务,不能为空
执行流程:
1、如果线程池当前线程数量少于corePoolSize,则addWorker(command, true)创建新worker线程,如创建成功返回,如没创建成功,则执行后续步骤;
addWorker (command, true)失败的原因可能是:
A、线程池已经shutdown,shutdown的线程池不再接收新任务
B、workerCountOf© < corePoolSize 判断后,由于并发,别的线程先创建了worker线程,导致workerCount >= corePoolSize
2、如果线程池还在running状态,将task加入workQueue阻塞队列中,如果加入成功,进行double-check,如果加入失败(可能是队列已满),则执行后续步骤;
double-check主要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值