JAVA线程池 -clt设计与分析

news/2024/5/10 21:39:43/文章来源:https://blog.csdn.net/ChengHuanHuaning/article/details/127738087

1. 前言

  1. ctl 是线程池源码中常常用到的一个变量。
  2. 它的主要作用是记录线程池的生命周期状态和当前工作的线程数。
  3. 作者通过巧妙的设计,将一个整型变量按二进制位分成两部分,分别表示两个信息。

2. 源码解析

源码部分

    /*** 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** In order to pack them into one int, we limit workerCount to* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2* billion) otherwise representable. If this is ever an issue in* the future, the variable can be changed to be an AtomicLong,* and the shift/mask constants below adjusted. But until the need* arises, this code is a bit faster and simpler using an int.** The workerCount is the number of workers that have been* permitted to start and not permitted to stop.  The value may be* transiently different from the actual number of live threads,* for example when a ThreadFactory fails to create a thread when* asked, and when exiting threads are still performing* bookkeeping before terminating. The user-visible pool size is* reported as the current size of the workers set.** The runState provides the main lifecycle control, taking on values:**   RUNNING:  Accept new tasks and process queued tasks*   SHUTDOWN: Don't accept new tasks, but process queued tasks*   STOP:     Don't accept new tasks, don't process queued tasks,*             and interrupt in-progress tasks*   TIDYING:  All tasks have terminated, workerCount is zero,*             the thread transitioning to state TIDYING*             will run the terminated() hook method*   TERMINATED: terminated() has completed** The numerical order among these values matters, to allow* ordered comparisons. The runState monotonically increases over* time, but need not hit each state. The transitions are:** RUNNING -> SHUTDOWN*    On invocation of shutdown(), perhaps implicitly in finalize()* (RUNNING or SHUTDOWN) -> STOP*    On invocation of shutdownNow()* SHUTDOWN -> TIDYING*    When both queue and pool are empty* STOP -> TIDYING*    When pool is empty* TIDYING -> TERMINATED*    When the terminated() hook method has completed** Threads waiting in awaitTermination() will return when the* state reaches TERMINATED.** Detecting the transition from SHUTDOWN to TIDYING is less* straightforward than you'd like because the queue may become* empty after non-empty and vice versa during SHUTDOWN state, but* we can only terminate if, after seeing that it is empty, we see* that workerCount is 0 (which sometimes entails a recheck -- see* below).*/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 bitsprivate 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 ctlprivate 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 维护两个值:运行状态(runState)和线程数量 (workerCount) 其中高3位保存runState,低29位保存workerCount当前池线程数。如下代码所示:

  // 初始化ctl值为0,并设置状态为RUNNINGprivate final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));// 结果:32-3=29,就用来表示分隔runState 和workerCount 的位数// 之所以-3,因为线程池的生命周期有 5 个状态,为了表达这 5 个状态,我们需要 3 个二进制位。private static final int COUNT_BITS = Integer.SIZE - 3;// 结果:000 1   1111 1111 1111 1111 1111 1111 1111private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

目前我们已经有了三个变量信息(如何使用这三个值,请看下文):

  • clt:保存了运行状态(runState)和线程数量 (workerCount) 的值
  • COUNT_BITS:29,分隔runState 和workerCount 的位数
  • CAPACITY(2进制):000 1 1111 1111 1111 1111 1111 1111 1111,前三位为0,后29位为1

可能你对【CAPACITY】二进制的值有所疑惑,那么我们来看看推演过程

1. (1 << COUNT_BITS) - 1可平替为  (1 << 29) - 1
2. 首先看 1 的二进制代表 	0000 0000 0000 0000 0000 0000 0000 00013. 向左移 29 位得到 		0010 0000 0000 0000 0000 0000 0000 00004. 数据减 1 得到 			0001 1111 1111 1111 1111 1111 1111 1111

我们都知道线程池有五个状态,它在线程池中分布是如下表示的:

  • RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务。
  • SHUTDOWN:指调用了 shutdown() 方法,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。
  • STOP:指调用了 shutdownNow() 方法,不再接受新提交的任务,同时抛弃阻塞队列里的所有任务并中断所有正在执行任务。
  • TIDYING: 所有任务都执行完毕,workerCount 有效线程数为 0。
  • TERMINATED:终止状态,当执行 terminated() 后会更新为这个状态。

在这里插入图片描述

源码如下

	// 结果29private static final int COUNT_BITS = Integer.SIZE - 3;// runState is stored in the high-order bitsprivate 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;

我们来推算下他们值(2进制表示)


/*** 各个值的二进制表示:** 1111 1111 1111 1111 1111 1111 1111 1111 (-1) * 0000 0000 0000 0000 0000 0000 0000 0000 (0) * 0000 0000 0000 0000 0000 0000 0000 0001 (1) * 0000 0000 0000 0000 0000 0000 0000 0010 (2) * 0000 0000 0000 0000 0000 0000 0000 0011 (3)** 【分析】:* 初始容量值,高三位全是0,低29位全是1;后续操作会以此为基础进行操作* CAPACITY(29)                000 1   1111 1111 1111 1111 1111 1111 1111**              ---------------3位-1位 -28位---* 【前三位,表明状态位,后29位表明线程个数,即 2^29 - 1 基本够用了】** RUNNING(-536870912)         111 0    0000 0000 0000 0000 0000 0000 0000* SHUTDOWN(0)                 000 0    0000 0000 0000 0000 0000 0000 0000* STOP(536870912)             001 0    0000 0000 0000 0000 0000 0000 0000* TIDYING(1073741824)         010 0    0000 0000 0000 0000 0000 0000 0000* TERMINATED(1610612736)      011 0    0000 0000 0000 0000 0000 0000 0000* * /


根据以上分析得,我们只需要重点关注高三位:

  • RUNNING:对应的高3位值是111
  • SHUTDOWN:对应的高3位值是000。
  • STOP: 对应的高3位值是001。
  • TIDYING:对应的高3位值是010。
  • TERMINATED:对应的高3位值是011。

打包函数与拆包函数

拆包函数

上面我们曾说过,线程池内部使用一个原子整型变量 ctl 维护两个值:运行状态(runState)和线程数量 (workerCount) 其中高3位保存runState,低29位保存workerCount当前池线程数
这样我们就可以知道这两个方法的目的:获取当前参数的高3位和低29位

    // 获取当前运行状态private static int runStateOf(int c) { return c & ~CAPACITY; }// 获取工作线程数private static int workerCountOf(int c) { return c & CAPACITY; }

让我们复习下&(与运算): 只要有0则为0
让我们复习下|(或运算): 只要有1则为1

运行状态
c & ~CAPACITY :意为CAPACITY取反后与c进行&操作,得到当前入参二进制的前三位(运行状态)

我们以c为【RUNNING】状态举例
CAPACITY结果			000  1   1111 1111 1111 1111 1111 1111 1111
~CAPACITY取反结果		111  0  0000 0000 0000 0000 0000 0000 0000 (前三位全为1,后29位全为0)
c的结果					111  X  XXXX XXXX XXXX XXXX XXXX XXXX XXXX (由上诉可知RUNNING高3位值是111)
c & ~CAPACITY的结果		111  0  0000 0000 0000 0000 0000 0000 0000 (与RUNNING对应的结果一致)

获取线程数量

我们以2个线程数来举例

CAPACITY结果			000 1 1111 1111 1111 1111 1111 1111 1111
2的二进制表示			XXX 0 0000 0000 0000 0000 0000 0000 0010
c & CAPACITY的结果		000 0 0000 0000 0000 0000 0000 0000 0010

打包函数

// rs 运行状态
// wc 工作线程个数
private static int ctlOf(int rs, int wc) { return rs | wc; }

对 runState 和 workerCount 进行 | (按位或)操作来得到 ctl 变量
就是因为 runState 的高 3 位为有效信息,而 workerCount 的低 29 位为有效信息,合起来正好得到一个含 32 位有效信息的整型变量。

为什么之前提到的生命周期常量要在 -1 ~ 3 的基础上再左移 29 位,因为不在常量初始化处左移的话就要在拆包/打包的时候右移来保证取到的是正确的数值。然而拆包/打包操作是要经常进行的,而常量的初始化只有一次。两下对比,明显在初始化时左移是效率更高的选择。

引用文章

详解Java线程池的ctl(线程池控制状态)【源码分析】
线程池(二、ctl 的设计分析)
一文读懂线程池的实现原理

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pytorch学习笔记6——时间序列RNN

Pytorch学习笔记6——时间序列 判断态度的二分类问题&#xff1a; 对于长句子不适合&#xff0c;因此需要权值共享&#xff1a; 对于某一层&#xff1a; 上一步输出作为下一步输入&#xff0c;存储语境信息&#xff1a; 自我更新语境信息ht ht是最后一次送入得到的m…

提出创意的方法

提出创意的方法 “我不擅长想出创意。” 你是否经常对自己说这样的话? 在公司的会议和学校的集体工作中总是提不出好点子。 即使提出了很多创意&#xff0c;也会偏向于相同的创意&#xff0c;无法得出有趣的提案。 为了打破这样的状况&#xff0c;这次的博客介绍创造性想法…

排序算法:插入排序、冒泡排序、选择排序、希尔排序、堆排序、快速排序

排序算法相关总结&#xff0c;涉及的排序算法有&#xff1a;插入排序、冒泡排序、选择排序、希尔排序、堆排序、快速排序。 这里写目录标题1.插入排序2.冒泡排序3.选择排序4.希尔排序5.堆排序6.快速排序总结稳定性概念: 假定在待排序的记录序列中&#xff0c;存在多个具有相同的…

缓冲区的管理

文章目录什么是缓冲区&#xff1f;有什么作用&#xff1f;单缓冲单缓冲和双缓冲通信时的区别循环缓冲区缓冲池什么是缓冲区&#xff1f;有什么作用&#xff1f; 缓冲区是一个存取区域&#xff0c;可以由专门的硬件寄存器组成&#xff0c;也可以用内存作为缓冲区&#xff0c;本节…

Python中的几种推导式用法,先收藏再说......

嗨害大家好鸭&#xff01;我是小熊猫❤ 今天我们来整点非常干的干货&#xff1a; 源码、资料电子书点击此处 Python 推导式是一种独特的数据处理方式&#xff0c; 可以从一个数据序列构建另一个新的数据序列的结构体。 Python 支持各种数据结构的推导式&#xff1a; 列表(li…

D. Maximum Sum on Even Positions(最大连续字段和)

Problem - 1373D - Codeforces 题意: 给你一个由n个整数组成的数组a。数组的索引从零开始&#xff08;即第一个元素是a0&#xff0c;第二个是a1&#xff0c;以此类推&#xff09;。 你最多可以扭转这个数组的一个子数组&#xff08;连续子段&#xff09;。回顾一下&#xff0c…

[附源码]计算机毕业设计JAVAjsp不回头药店药品管理系统

[附源码]计算机毕业设计JAVAjsp不回头药店药品管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SS…

【Prometheus】什么是prometheus?prometheus简介

本文目录 一、什么是prometheus&#xff1f; 二、体系结构概述 三、适用场景 四、不适用的场景 一、什么是prometheus&#xff1f; Prometheus官网 Prometheus开源代码 From metrics to insight Power your metrics and alerting with the leading open-source monitoring…

随想录一刷Day48——动态规划

文章目录Day48_动态规划29. 打家劫舍30. 打家劫舍 II31. 打家劫舍 IIIDay48_动态规划 29. 打家劫舍 198. 打家劫舍 思路&#xff1a; 题目的关键是&#xff0c;不能偷相邻的两个屋子&#xff0c;即只能偷上一个屋子不偷当前屋子&#xff0c;或者不偷上一个屋子偷当前屋子。 d…

LeetCode刷题(python版)——Topic57插入区间

一、题设 给你一个 无重叠的 &#xff0c;按照区间起始端点排序的区间列表。 在列表中插入一个新的区间&#xff0c;你需要确保列表中的区间仍然有序且不重叠&#xff08;如果有必要的话&#xff0c;可以合并区间&#xff09;。 示例 1&#xff1a; 输入&#xff1a;interva…

ServletConfig和ServletContext接口

一、ServletConfig接口详解 1、简介 Servlet 容器初始化 Servlet 时&#xff0c;会为这个 Servlet 创建一个 ServletConfig 对象&#xff0c;并将 ServletConfig 对象作为参数传递给 Servlet 。通过 ServletConfig 对象即可获得当前 Servlet 的初始化参数信息。一个 Web 应用中…

【仿牛客网笔记】 Redis,一站式高性能存储方案——Redis入门

Redis可以开发对性能要求较高的功能。还可以利用Redis重构我们现有的功能。 NoSQL关系型数据库之外的统称。 快照有称为RDB 以快照的形式 不适合实时的去做&#xff0c;适合一段时间做一次。 日志又称AOF 以日志的形式每执行一次就存入到硬盘中&#xff0c;可以做到实时的存储以…

【python的静态方法,classmethod方法和__call___魔法方法】

classmethod魔法方法和staticmethodstaticmethod&#xff0c;静态方法classmethod&#xff0c;绑定类方法__call__&#xff0c;可调用类类方法staticmethod&#xff0c;静态方法 在python中&#xff0c;使用静态方法可以实现不需要实例化对象的绑定就可以直接调用的函数&#…

【Unity3D】游戏物体操作 ④ ( 选中多个游戏物体操作 | 复制选中物体 | 聚焦选中物体 | 激活、禁用选中物体 | 对齐选中物体 )

文章目录一、选中多个游戏物体操作1、Scene 场景窗口选中多个物体2、Hierarchy 层级窗口选中多个物体二、复制选中物体1、使用 " Ctrl D " 快捷键复制物体2、使用 右键菜单 | Duplicate 选项复制三、聚焦选中物体四、激活、禁用选中物体五、对齐选中物体一、选中多个…

计算机组成原理浮点数表示

浮点数表示 浮点数的表示分为阶码和尾数&#xff1b; 比如3.026*1011;阶码是11;尾数是3.026&#xff1b; 对于阶码&#xff1a; 阶符为正&#xff0c;小数点向后移n位&#xff08;n表示阶的大小&#xff09;; 阶符为负&#xff0c;小数点向前移n位&#xff08;n表示阶的大小&a…

AttributeError: ‘bytes‘ object has no attribute ‘encode‘异常解决方案

AttributeError: bytes object has no attribute encode是&#xff1a;“字节”对象没有属性的编码的意思。 很明显&#xff0c;是编码格式的问题&#xff0c;例如&#xff1a;已经是byte格式的字符串类型&#xff0c;二次进行encode的时候就会出现这个bug&#xff0c;示例如下…

【猿创征文】Vue3 企业级优雅实战 - 组件库框架 - 1 搭建 pnpm monorepo

前两篇文章分享了基于 vite3 vue3 的组件库基础工程 vue3-component-library-archetype 和用于快速创建该工程的工具 yyg-cli&#xff0c;但在中大型的企业级项目中&#xff0c;通常会自主搭建这些脚手架或加速器。优雅哥希望每位前端伙伴能知其所以然&#xff0c;故接下来的文…

基础IO(下)——Linux

文章目录1. 理解文件系统1.2 背景知识1.2 inode vs 文件名1.3 软硬链接2. 动态库和静态库2.1 静态库.a2.1.1 如果想写一个库&#xff1f;&#xff08;编写库的人的角度&#xff09;2.1.2如果我把库给别人&#xff0c;别人怎么用呢&#xff1f;&#xff08;使用库的人的角度&…

中医-通过舌象判断身体状况

本文分享通过舌象判断身体的整体状况&#xff08;中医角度&#xff09;&#xff0c;得出一个可供辨证的参考&#xff0c;并且可以根据舌象做出相关的饮食调整&#xff0c;本文主讲理论&#xff0c;相关舌象图片易引人不适&#xff0c;如需找相关图片&#xff0c;可根据本文中的…

git rebase实战

例如&#xff0c; 在B分支上做rebase。 git rebase 之前确保当前分支是最新的。 切换到B分支&#xff1a; 1.git rebase origin/master(以origin/master分支为基线&#xff0c;合入master分支的修改到origin/master)此时会提示冲突文件 2.对冲突文件进行修改 3.git add 4.git …