【线程池】线程池的ctl属性详解

news/2024/5/20 9:11:35 标签: 线程池, 多线程, JUC, 并发编程, ctl

目录

ctl%E4%BB%8B%E7%BB%8D-toc" style="margin-left:0px;">一、ctl介绍

ctl%E6%BA%90%E7%A0%81-toc" style="margin-left:0px;">二、线程池ctl源码

ctl%E5%88%86%E6%9E%90-toc" style="margin-left:0px;">三、线程池ctl分析

ctlOf(int%20rs%2C%20int%20wc)%20%7B%20return%20rs%20%7C%20wc%3B%20%7D-toc" style="margin-left:40px;">1、private static int ctlOf(int rs, int wc) { return rs | wc; }

ctl%20%3D%20new%20AtomicInteger(ctlOf(RUNNING%2C%200))%3B%C2%A0-toc" style="margin-left:40px;">2、private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 

3、private static int runStateOf(int c)     { return c & ~CAPACITY; }

4、private static int workerCountOf(int c)  { return c & CAPACITY; }

ctl%E5%A6%82%E4%BD%95%E7%AE%A1%E7%90%86%E6%B1%A0%E7%8A%B6%E6%80%81%E5%92%8C%E7%BA%BF%E7%A8%8B%E6%95%B0%C2%A0-toc" style="margin-left:0px;">四、线程池ctl如何管理池状态和线程数 


一、ctl介绍

ThreadPoolExecutor中有一个控制状态的属性叫ctl,它是一个AtomicInteger类型的变量,它包含两个概念:

  1. workerCount:表明当前有效的线程数
  2. runState:表明当前线程池的状态,是否处于Running,Shutdown,Stop,Tidying,Terminate五种状态。

为了把这两种状态放到一个int值(共32位)中保存,代码中限定了workerCount的值是2^29-1,因为还有五种状态需要表示,至少需要3位才能表示五种状态(3位二进制数最大能到6),所以会有29位来表示workerCount,而剩下的3位来表示当前线程池的状态。

从上面的代码中可以看出COUNT_BITS这个属性就是Integer.SIZE-3,也就是29。说明线程数所占位数为29位,而CAPACITY得到的就是1向左无符号移29位-1,得到的就是低28位全是1的536870911。而看到上图下面的五个状态,分别是-1,0,1,2,3向左无符号移29位。

位数计算

  • 从上图可以看到workerCountOf这个函数传入ctl之后,是通过ctl & CAPACITY操作来获取当前运行线程总数的。也就是RunningState | WorkCount & CAPACITY,算出来的就是低28位的值。因为CAPACITY得到的就是高3位(29-31位)位0,低28位(0-28位)都是1,所以得到的就是ctl中低28位的值。
  • 而runStateOf这个方法的话,算的就是RunningState | WorkCount & ~CAPACITY,高3位的值,因为~CAPACITY是CAPACITY的取反,所以得到的就是高3位(29-31位)为1,低28位(0-28位)为0,所以通过&运算后,所得到的值就是高3为的值。
  • 从而理解了ctl中是高3位作为状态值,低28位作为线程的线程数量来进行存储的原因。

ctl%E6%BA%90%E7%A0%81" style="margin-left:0;">二、线程池ctl源码

打开ThreadPoolExecutor的源码(我裁剪掉了一部分),一开始就会发现:

public class ThreadPoolExecutor extends AbstractExecutorService {
        /** 我以一个字节8位来简化解释线程池对运行状态和当前有效线程个数的原子管理方案
    
         * 线程池当中,用一个ctl原子变量包装了高3位的运行状态和低5位的线程个数
         *
         *   运行状态:  线程池初始化后,就处于该状态:此时,线程池可以接受新任务并且处理任务
         *   关闭状态:  调用shutdown()方法时,就处于该状态:此时,shutdown()方法之后不能再提交新任务,线程池会把shutdown()方法之前提交的任务按照线程池工作原理的步骤都处理完毕。(请参考我的博客:线程池工作原理)
         *   停止状态:  调用shutdownNow()方法时,就处于该状态:此时,shutdownNow()方法之后不能再提交新任务,线程池不处理已经提交到任务队列中的任务,线程池尝试中断正在执行的工作线程
         *   整理状态:  线程池内部自己使用的状态:当线程池queue任务队列为空,hashset<worker>为空时,就是该状态,该状态是由关闭状态/停止状态转变而来的。当处于整理状态时,线程池会调用terminated()钩子方法
         *   终结状态:  当钩子方法terminated()执行完毕之后,线程池由整理状态转变为终结状态。钩子方法是线程池自动调用的。
         *
         *   在线程池终结状态之前,可以调用awaitTermination()阻塞方法,使当前主线程阻塞,直至线程状态转变为终结状态
         */ 
    
     /**
     * 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
      *
      * 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, workers is zero,the thread transitioning to state TIDYING
      *   TERMINATED: terminated() has completed
        *
        * 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,will run the terminated() method
        * 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.
         */
        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%E5%88%86%E6%9E%90" style="margin-left:0;">三、线程池ctl分析

负数的表示方法:补码表示法(原码的反码+1

我以8字节举例(同理适应于32位或64位机器)

上源码:我以8字节举例(同理适应于32位或64位机器)

private static final int COUNT_BITS = Integer.SIZE - 3;            //8-3=5 活跃线程支持5位来表示个数
private static final int CAPACITY   = (1 << COUNT_BITS) - 1; //1左移5位-1(1*2的5次方-1),也就是31个线程(低5位)   也就是00000001左移5位是00100000,然后再减00000001得到最终结果00011111将低5位标识线程数的部分保留

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;       // -1即11111111 左移5位后为11100000 表示运行状态(高3位)
private static final int SHUTDOWN   =  0 << COUNT_BITS;    //  0即00000000 左移5位后为00000000 表示关闭状态(高3位)
private static final int STOP       =  1 << COUNT_BITS;           //  1即00000001 左移5位后为00100000 表示停止状态(高3位)
private static final int TIDYING    =  2 << COUNT_BITS;         //  2即00000010 左移5位后为01000000 表示关闭状态(高3位)
private static final int TERMINATED =  3 << COUNT_BITS;   //  3即00000011 左移5位后为01100000 表示关闭状态(高3位)

不难发现,高3位很好的表示5种状态 000 SHUTDOWN,001 STOP,010 TIDYING,011 TERMINATED,111 RUNNING

ctlOf(int%20rs%2C%20int%20wc)%20%7B%20return%20rs%20%7C%20wc%3B%20%7D" style="margin-left:0;">1private static int ctlOf(int rs, int wc) { return rs | wc; }

假如:当前线程池是运行状态 rs = -1 并且有效线程是3个(ws),那么ctlOf(rs,wc)方法的逻辑是什么意思呢?rs | wc 的翻译如下:

11100000 (运行状态)

或             (0|1为1,1|1为1,0|0为0,也就是说,或的位运算,有1就为真)

00000011(线程个数)

的值为:11100011 表示ctl当前的值:有3个线程,线程池处于运行状态

该方法用于初始化ctl

ctl%20%3D%20new%20AtomicInteger(ctlOf(RUNNING%2C%200))%3B%C2%A0" style="margin-left:0;">2private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 

由以上的ctlOf()举例分析,知晓了,初始化的ctl值为11100000,高3位表示线程池目前处于运行状态,低5位表示线程0个

3private static int runStateOf(int c)     { return c & ~CAPACITY; }

这里的c是ctl.get()得到的原子值(AtomicInteger原子类中的volatile变量),也可以理解为当前ctl的值,这个值的高3位表示线程池的运行状态,低5位表示当前的线程个数。

c & ~CAPACITY 翻译如下:

假如c的值当前就是上面的举例:11100011 表示有3个线程,线程池处于运行状态

~CAPACITY 的运算即为:00011111求反,得值为11100000

c & ~CAPACITY

11100011(高3位表示运行状态)

与            (0&1为0,1&1为1,0&0为0,也就是说,与的位运算,有0必为假)

11100000

得值为:11100000 会发现,runStateOf()方法的目的就是高3位原是什么样,现运算后还是什么样,剔除掉了低5位的影响,我们通过runStateOf()方法拿到了纯的当前状态的值xxx00000 xxx刚好是:当初规定好的高3位表示当前的线程池状态

4private static int workerCountOf(int c)  { return c & CAPACITY; }

有了以上 c & ~CAPACITY 翻译后,这里就较好理解,下面翻译下 c & CAPACITY

假如c的值当前就是上面的举例:11100011 表示有3个线程,线程池处于运行状态

c & CAPACITY

11100011(高3位表示运行状态)

与            (0&1为0,1&1为1,0&0为0,也就是说,与的位运算,有0必为假)

00011111

ctl%E5%A6%82%E4%BD%95%E7%AE%A1%E7%90%86%E6%B1%A0%E7%8A%B6%E6%80%81%E5%92%8C%E7%BA%BF%E7%A8%8B%E6%95%B0%C2%A0" style="margin-left:0;">四、线程池ctl如何管理池状态和线程数 

翻看ThreadPoolExecutor的源码会发现:

ctl的初始化:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 
private static int ctlOf(int rs, int wc) { return rs | wc; }

ctl线程池运行期间,有大量的方法都调用了:

ctl.compareAndSet(expect, update); //这个操作是原子操作,表示设置当前ctl的值。

可参考AtomicInteger原子类的compareAndSet()方法

public final boolean compareAndSet(int expect, int update)

 相关文章:线程池】Java的线程池
                  【线程池】Java线程池的核心参数
                  【线程池】Executors框架创建线程池        
                                  【线程池】ScheduledExecutorService接口和ScheduledThreadPoolExecutor定时任务线程池使用详解                 【线程池线程池的拒绝策略(饱和策略) 


http://www.niftyadmin.cn/n/1005551.html

相关文章

打造自己的分布式MinIO对象存储

MinIO是一个对象存储解决方案&#xff0c;它提供了一个与Amazon Web Services S3兼容的API&#xff0c;并支持所有核心S3特性。MinIO旨在部署在任何地方——公共云或私有云、裸机基础架构、协调环境和边缘基础架构。 分布式MinIO如何工作 Server Pool由多个Minio服务节点与其附…

Qt QGraphics导入背景图并绘制图形,画布移动、缩放、图形旋转等

前言 之前写过一篇博文《Qt鼠标拖动绘制基本几何图形》 &#xff0c;这是介绍使用QGraphic中利用鼠标事件实现基本几何图形的绘制&#xff0c;支持直线、矩形、圆形、椭圆&#xff0c;本次是在此基础上进行扩展&#xff0c;实现背景图导入&#xff0c;并在图片上进行几何图形绘…

C++ 模板 using, Concept

template <class T> using IteratorMemberFunction T::iterator(T::*)(); 用IteratorMemberFunction<T>(&T::begin)从T中获取成员函数的函数指针 使用decltype获取类型&#xff0c;判断该类型是否为一个成员函数&#xff0c;不包含 begin 就会违反这个约…

SpringBoot(实用开发篇)

SpringBoot实用开发篇 第三方属性bean绑定 ConfigurationProperties 使用ConfigurationProperties为第三方bean绑定属性 配置文件 datasource:driverClassName: com.mysql.jdbc.Driver servers:ipAddress: 192.168.0.1port: 80timeout: -1ServerConfig类&#xff1a; Dat…

知识图谱实战应用18-知识图谱结合图神经网络GNN的实战应用,模型搭建与训练

大家好,我是微学AI,今天给大家介绍一下知识图谱实战应用18-知识图谱结合图神经网络GNN的实战应用,模型搭建与训练,本文将详细介绍如何基于Py2neo的知识图谱结合图神经网络(Graph Neural Network, GNN)实现一个应用项目。我们将首先导入CSV数据到Neo4j图数据库,然后利用G…

SpringBoot3之GraalVM之Windows详细安装及使用教程

配置Maven环境变量 我直接使用的是IDEA plugins文件夹下的maven 新建MAVEN_HOME环境变量 Path环境变量追加 %MAVEN_HOME%\bin安装Visual Studio Community 因为GraalVM需要调用操作系统的底层工具&#xff0c;而Windows底层工具是VisualStudio&#xff0c;所以我们要先下载…

4G/wifi/lora投入式无线液位传感变送器 mqtt/http协议对接云平台

1.产品概述 DAQ-GP-TLL4G无线液位传感器终端是上海数采物联网科技有限公司推出的一款无线液体水位测量产品。原理是利用扩散硅片上的一个惠斯通电桥&#xff0c;被测介质&#xff08;气体或液体&#xff09;施压使桥壁电阻值发生变化&#xff08;压阻效应&#xff09;&#xff…

网络安全的重要性

网络安全是指网络系统的硬件、软件及其系统中的数据受到保护&#xff0c;不受偶然的或者恶意的原因而遭到破坏、更改、泄露&#xff0c;系统连续可靠正常地运行&#xff0c;网络服务不中断。 网络安全从其本质上来讲就是网络上的信息安全。 从广义来说&#xff0c;凡是涉及到…