ThreadPoolExecutor的源码解析

news2024/12/24 20:35:54

ThreadPoolExecutor的源码解析

线程池的核心属性

ctl:当前的ctl就是一个int类型的数值,内部是基于AtomicInteger套了一层,进行运算时,是原子性

ctl表示的线程池的两种核心状态:

  1. 线程池的状态: ctl高3位标识线程池的状态
  2. 工作线程的状态:ctl低29位,表示工作线程的个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 

COUNT_BITS: 声明了一个常量, COUNT_BITS = 29
Integer.SIZE:在获取Integerbit个数

private static final int COUNT_BITS = Integer.SIZE - 3;

CAPACITY:就是当前工作线程能记录工作线程的最大个数

private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

线程池五种状态的表示

1.RUNNING:

只有此状态表示线程池没有问题**,可以正常接受任务处理**

111**:高三位(代表running状态),running表示可以处理任务,或者阻塞队列的任务

private static final int RUNNING    = -1 << COUNT_BITS;

2.SHUTDOWN:

000:代表shutdown的状态,不会接受新任务,正在处理的任务正常进行,阻塞队列的任务也会完成

private static final int SHUTDOWN   =  0 << COUNT_BITS;

3.STOP:

001:代表我们的stop状态,不会接受新任务,正在处理任务的线程会被中断,阻塞队列的任务一个不管

private static final int STOP   =  1 << COUNT_BITS;

4.TIDYING:

010:代表TIDYING状态,这个状态是shutdown或者stop的状态转换过来的,代表当前线程马上关闭,就是一个过渡状态

private static final int TIDYING    =  2 << COUNT_BITS;

5.TERMINATED

011:代表TERMINATED状态,这个状态是TIDYING状态转换过来的转换过来只需要执行一个terminated方法

private static final int TERMINATED =  3 << COUNT_BITS;

线程池的核心属性整体源码解析代码

//当前的ctl就是一个int类型的数值,内部是基于AtomicInteger套了一层,进行运算时,是原子性的
//ctl表示的线程池的两种核心状态:
//线程池的状态: ctl的高3位标识线程池的状态
//工作线程的状态:ctl的低29位,表示工作线程的个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 
//声明了一个常量, COUNT_BITS = 29
//Integer.SIZE:在获取Integer的bit的个数
private static final int COUNT_BITS = Integer.SIZE - 3;
//CAPACITY就是当前工作线程能记录工作线程的最大个数
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 线程池状态的表示:
//当前的五个状态:只有running状态表示线程池没有问题,可以正常接受任务处理
///111:高三位(代表running状态),running表示可以处理任务,或者阻塞队列的任务
private static final int RUNNING    = -1 << COUNT_BITS;
///000:代表shutdown的状态,不会接受新任务,正在处理的任务正常进行,阻塞队列的任务也会完成
private static final int SHUTDOWN   =  0 << COUNT_BITS;
//001:代表我们的stop状态,不会接受新任务,正在处理任务的线程会被中断,阻塞队列的任务一个不管
private static final int STOP       =  1 << COUNT_BITS;
//010:代表TIDYING状态,这个状态是shutdown或者stop的状态转换过来的,代表当前线程马上关闭,就是一个过渡状态
private static final int TIDYING    =  2 << COUNT_BITS;
//011:代表TERMINATED状态,这个状态是TIDYING状态转换过来的转换过来只需要执行一个terminated方法
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl:在使用下面的三个值时,需要传进来ctl
//拿到高三位的值
//基于&运算的特点,保证只会拿到ctl的高三位值
private static int runStateOf(int c)     { return c & ~CAPACITY; }
//基于&运算的特点,保证只会拿到ctl的低29位的值
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

线程池的状态转换图

在这里插入图片描述

ThreadPoolExecutor的有参构造

//无论调用哪个有参构造,都会执行当前的有参构造
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    //健壮性校验
    	//核心线程个数允许为0,最大线程数必须大于0,最大线程数要大于等于核心线程数
    	//非核心线程的最大空闲时间,可以等于0
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            //不满足要求就抛出参数异常
            throw new IllegalArgumentException();
    	//阻塞队列,线程工厂,拒绝策略都不允许为null,为null就抛就抛空指针异常
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
    	//此行为系统资源访问策略,和线程池核心业务关系不大
        this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
    	//各种赋值,在JUC包下,几乎所有涉及到线程挂起的操作,单位都是用纳秒
    	//有参构造的值,都赋值给我们成员变量
    	//doug lea习惯就是将成员变量作为局部变量单独操作
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

ThreadPoolExecutor的execute方法

execute是提交任务到线程池的核心方法,很重要

这里我强烈大家跟着敲一遍注释,绝对收获受益匪浅

线程池的执行流程其实就是再说execute方法内部作了那些判断

源码解析:

execute():是提交任务到线程池的核心方法
参数:command就是提交过来的任务

public void execute(Runnable command) {

提交的任务不能为null,否则抛空指针异常

if (command == null)   throw new NullPointerException();

ctl.get():获取核心属性ctl,用于后面的判断

int c = ctl.get();

解释: 如果工作线程个数小于核心线程数,满足要求,添加核心工作线程

workerCountOf(c):查询工作线程个数

corePoolSize:核心线程数

addworker(任务,是核心线程吗): 是否添加工作线程

 if (workerCountOf(c) < corePoolSize) {
     //addworker(任务,是核心线程吗)
     //addworker返回true,代表添加工作线程成功
     //addworker返回false,代表添加工作线程失败
     //addWorker会给予线程池状态以及工作线程个数做判断,查看能否添加工作线程 
     if (addWorker(command, true))
         //工作线程构建出来了,任务也交给command去处理了
         return;
     //说明线程池状态或者是工作线程个数发生了变化,导致添加失败,重新获取一次ctl
     c = ctl.get();
 }

添加核心线程失败,走下面的步骤(上述的步骤都失败,走下面的)

isRunning(c):判断线程池状态是否是running

workQueue.offer(command):基于阻塞队列的offer方法,将任务添加到阻塞队列

在此阻塞队列添加收否成成功又有两种情况:

  • 成功添加
    1. 重新获取ctl
    2. 判断线程池的状态是否为running:
      • 否:任务从阻塞队列移除,并直接执行拒绝策略
      • 是: 继续下一步
    3. 查看工作线程数是否是0个
      • 是:添加一个非核心线程处理
      • 否: 结束本次流程
//添加核心线程失败,走下面的步骤
//判断线程池状态是否是running,如果是,正常基于阻塞队列的offer方法,将任务添加到阻塞队列

if (isRunning(c) && workQueue.offer(command)) {
    //如果任务添加到阻塞队列成功,直接走if内部
    //如果任务在扔到阻塞队列之前线程池的状态改变了,
    //重新获取ctl
    int recheck = ctl.get();
    //如果线程池的状态不是running,将任务从阻塞队列移除,并直接执行拒绝策略
    if (! isRunning(recheck) && remove(command))
        reject(command);
    //阻塞队列有我刚刚放进去的任务
    //查看工作线程数是否是0个
    //如果工作线程为0个,需要添加一个非核心工作线程去处理阻塞队列中的任务
    //发生这种情况由两种:
    //1.构建线程池时,核心线程为0个
    //2.即便有核心线程,也可以设置核心线程允许超时,设置allowsCoreThreadTimeOut等于true,表示核心线程可以超时
    else if (workerCountOf(recheck) == 0)
        //为了避免阻塞队列的任务饥饿,添加一个非核心线程处理
        addWorker(null, false);
}

上述条件都不符合,直接直接执行决绝策略,结束

//任务添加到阻塞队列失败
    	//构建一个非核心工作线程
    	//如果添加非核心线程成功,直接结束
        else if (!addWorker(command, false))
            //添加 失败,执行拒绝策略
            reject(command);

完整源码解析

//提交任务到线程池的核心方法
//command就是提交过来的任务
public void execute(Runnable command) {
    //提交的任务不能为null
        if (command == null)
            throw new NullPointerException();
   		//获取核心属性ctl,用于后面的判断
        int c = ctl.get();
        //如果工作线程个数小于核心线程数
        //满足要求,添加核心工作线程
        if (workerCountOf(c) < corePoolSize) {
            //addworker(任务,是核心线程吗)
            //addworker返回true,代表添加工作线程成功
            //addworker返回false,代表添加工作线程失败
            //addWorker会给予线程池状态以及工作线程个数做判断,查看能否添加工作线程 
            if (addWorker(command, true))
                //工作线程构建出来了,任务也交给command去处理了
                return;
            //说明线程池状态或者是工作线程个数发生了变化,导致添加失败,重新获取一次ctl
            c = ctl.get();
        }
    	//添加核心线程失败,走下面的步骤
    	//判断线程池状态是否是running,如果是,正常基于阻塞队列的offer方法,将任务添加到阻塞队列
    	
        if (isRunning(c) && workQueue.offer(command)) {
            //如果任务添加到阻塞队列成功,直接走if内部
            //如果任务在扔到阻塞队列之前线程池的状态改变了,
            //重新获取ctl
            int recheck = ctl.get();
            //如果线程池的状态不是running,将任务从阻塞队列移除,并直接执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //阻塞队列有我刚刚放进去的任务
            //查看工作线程数是否是0个
            //如果工作线程为0个,需要添加一个非核心工作线程去处理阻塞队列中的任务
            //发生这种情况由两种:
            //1.构建线程池时,核心线程为0个
            //2.即便有核心线程,也可以设置核心线程允许超时,设置allowsCoreThreadTimeOut等于true,表示核心线程可以超时
            else if (workerCountOf(recheck) == 0)
                //为了避免阻塞队列的任务饥饿,添加一个非核心线程处理
                addWorker(null, false);
        }
    	//任务添加到阻塞队列失败
    	//构建一个非核心工作线程
    	//如果添加非核心线程成功,直接结束
        else if (!addWorker(command, false))
            //添加 失败,执行拒绝策略
            reject(command);
    }

ThreadPoolExecutor的execute方法解析完整流程图

在这里插入图片描述
注意,到这里并没有完结撒花哈!~
正在更新中,但是上面的execute()执行流程图我强烈推荐大家跟着画一遍,很有收获的!!!
后面更新对addworker()方法源码的剖析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2197876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FlagVNE]——用于虚拟网络嵌入的灵活、可通用的强化学习框架

介绍 论文地址&#xff1a;https://arxiv.org/pdf/2404.12633 网络虚拟化&#xff08;NV&#xff09;是一种创新技术&#xff0c;在 5G 网络和云计算等领域日益受到关注。NV 可通过网络切片和共享基础设施在同一物理网络上部署多个用户提交的虚拟网络请求&#xff08;VNR&…

性能测试-JMeter(1)

性能测试工具 主流性能测试工具LoadrunnerJMeter JMeter环境安装JMeter功能概要JDK常用文件目录介绍JMeter元件和组件介绍元件的基本介绍组件的基本介绍 JMeter元件作用域和执行顺序JMeter第一个案例线程组HTTP请求查看结果树 JMeter参数化&#xff08;重点&#xff09;用户定义…

02_InFluxDb

InFluxDb 初始化初始化流程 交互InFluxDbWebUI交互 数据模型行协议添加标签数据格式 数据类型空格索引 初始化 初始化流程 用户 密码 组织名称 Bucket—mysql里面的数据库概念 交互InFluxDb 暂用了8086端口.提供了 http api WebUI交互 略... 数据模型 这是mysql里面的表…

1500元买哪款显卡好?对比一下,差别明显

在游戏过程中&#xff0c;显卡负责渲染游戏画面&#xff0c;将其转化为可视化的图像&#xff0c;并快速显示在屏幕上&#xff0c;确保游戏运行的流畅性和画面的质量。所以对于游戏电脑来说&#xff0c;显卡的重要性尤为突出。虽说在最近几年&#xff0c;显卡市场的“消费升级”…

算法:前缀和算法模版

一维前缀和 题目 链接&#xff1a;一维前缀和模版题 思路分析 一&#xff1a;暴力O(q * N) 对于每一次询问&#xff0c;我们都可以用一个循环计算[l,r]区间内的元素和&#xff0c; 时间复杂度&#xff0c;O(q * N) 每一次计算一个区间都需要去循环一次&#xff0c;这是不是…

2024年中国研究生数学建模什么时候出成绩(附避坑指南)

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 今年的华为杯已经于2024年9月20日——2024年9月25日完成&#xff0c;相信大家下…

40 C 语言结构体:结构体数据类型、结构体变量、访问结构体成员、结构体指针

目录 1 为什么需要结构体 2 什么是结构体 3 声明结构体类型 3.1 语法格式 3.2 案例演示 3.2.1 学生信息结构体 3.2.2 通讯录条目结构体 3.2.3 猫咪结构体 4 声明结构体变量 4.1 什么是结构体变量 4.2 声明结构体变量的常见方式 5 结构体和结构体变量的区别与联系 …

基于STM32的智能鱼缸自动喂食系统设计

引言 本项目设计了一个基于STM32的智能鱼缸自动喂食系统&#xff0c;能够按照预设的时间间隔自动投放饲料&#xff0c;同时监测鱼缸内的水温和光照情况。该系统通过电机控制喂食器的旋转来实现饲料投放&#xff0c;用户还可以通过按键实现手动喂食。该项目展示了STM32在定时控…

Spring Validation —— 参数校验框架

案例说明——后端校验注册表单字段 在编写注册功能时&#xff0c;需要考虑字段校验的情况&#xff0c;这时候可以采用 Spring提供的一套参数校验框架工具——Spring Validation。一下是使用的步骤&#xff1a; 1. 导入validation坐标 2. 在参数上添加 Pattern注解&#xff0c…

单细胞|Signac 进行 Motif 分析

单细胞|Signac 进行 Motif 分析 引言 本教程将指导您如何在Signac平台上进行DNA序列的基序(Motif)分析。会介绍两种基序分析的方法&#xff1a;一种是在一组差异可访问的峰值中寻找出现频率较高的基序&#xff1b;另一种是在不同细胞群组间进行基序活性的差异分析。 library(Si…

simpread-OpenAI推出Canvas:让ChatGPT成为更出色的项目协作者

引言 OpenAI在最新一轮融资中创下了VC融资历史上的记录&#xff0c;与此同时&#xff0c;他们也推出了一项令人瞩目的新功能——Canvas。Canvas是一个专门为项目协作设计的界面&#xff0c;旨在让ChatGPT成为更高效的项目协作者。在现有的ChatGPT应用之外&#xff0c;它打开了…

LC538 - 把二叉搜索树转换为累加树

文章目录 1 题目2 思路3 ACM模式参考 1 题目 https://leetcode.cn/problems/convert-bst-to-greater-tree/description/ 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09; 累加树&#…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-08

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-07 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-07目录1. GraphRouter: A Graph-based Router for LLM Selections摘要创新点算法模型实验效果结论推荐阅读指数 2. DOTS: Learni…

2024年诺贝尔物理学奖授予AI先驱 Hopfield 和Hinton,奖金1100万瑞典克朗,他们是ChatGPT的忠实用户

2024年诺贝尔物理学奖得主&#xff1a;AI领域的杰出科学家 获奖者 2024年诺贝尔物理学奖的获奖者是两位在人工智能&#xff08;AI&#xff09;尤其是机器学习技术方面做出杰出贡献的科学家&#xff1a;约翰霍普菲尔德和杰弗里欣顿。 瑞典皇家科学院在周二宣布&#xff0c;这两…

Shamiko模块:隐藏root

开启Zygisk 在安装Shamiko模块之前&#xff0c;需要使用面具开启Zygisk&#xff0c;在面具的设置里可以开启&#xff0c;开启后重启手机Zygisk生效。 开启后的样子 开启面具随机报名 在面具的设置里&#xff0c;找到“隐藏Magisk应用”&#xff0c;点击这个选项&#xff0…

ESP8266使用AT指令完成MQTT功能

ESP8266使用AT指令完成MQTT功能 在esp8266设备中烧录安信可的AT固件之后&#xff0c;进行AT指令完成信息发布&#xff0c;并最终实现在Homeassistant中发布传感器并设置传感器状态。 一、基础指令 以下是完整的步骤和对应的AT指令&#xff1a; 1. 配置ESP8266为Station模式 …

在线培训知识库管理系统:企业的明智之选

在当今这个快速变化的时代&#xff0c;企业之间的竞争日益激烈&#xff0c;知识更新速度之快前所未有。为了保持竞争力&#xff0c;企业不仅需要不断引入新技术、新方法&#xff0c;还需要确保员工能够及时掌握这些新知&#xff0c;将其转化为生产力。在此背景下&#xff0c;在…

codetop标签双指针题目大全解析(四),双指针刷穿地心!!!!!

复习复习复习&#xff01;&#xff01; 1.长度最小的子数组2.移动零3.盛水最多的容器4.旋转链表5.最接近的三数之和6.删除有序数组中的重复项7.返回倒数第k个节点的值8.四数之和9.验证回文串 1.长度最小的子数组 考滑动窗口的 要注意是大于等于不是等于 看错题目一顿调 class …

CNAI趋势下,打造一体化AI赋能平台

在数字化转型的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;已成为推动企业创新和转型的核心力量。云原生技术以其灵活性和可扩展性&#xff0c;为AI的应用和发展提供了坚实的基础。本文将探讨云原生人工智能&#xff08;CNAI&#xff09;如何为企业带来颠覆性的变革…

cnn突破八(两层卷积核bpnet网络扩展)

cnn突破七中x【&#xff1f;】怎么求&#xff1f;我们举个例子&#xff1a; 接着cnn突破七&#xff1a; hicnn【】来自temphicnn【】2*2最大池化&#xff1a; temphicnn[0]x[i0,j0,5*5方阵]*w1cnn[0-24]&#xff0c; hicnn是5*5的&#xff0c;temphicnn是10*10的&#xff0…