游乐游手机版
首页/系统平台/文章详情

编程之美:从线程池状态管理理解二进制操作原理

时间:2026-08-21 16:37
二进制操作在框架设计与底层实现中非常常见,利用二进制在不同场景下往往能够带来计算速度更快、内存占用更少等多方面优势;下面,我们结合线程池的状态管理机制,详细看一看如何通过操作二进制来管理状态,整个分析过程也能让我们感受到编程之美~线程池状态首先,为了让本文内容更加完整,我们先了解一下线程池的几种核心

二进制操作在框架设计与底层实现中非常常见,利用二进制在不同场景下往往能够带来计算速度更快内存占用更少等多方面优势;

下面,我们结合线程池的状态管理机制,详细看一看如何通过操作二进制来管理状态,整个分析过程也能让我们感受到编程之美~

线程池状态

首先,为了让本文内容更加完整,我们先了解一下线程池的几种核心状态,整理如下:

线程池状态一共分为5种RUNNINGSHUTDOWNSTOPTIDYINGTERMINATED

状态代表的含义

  • RUNNING:(运行中)接收新的task,并且处理正在队列中等待的task,不会中断正在执行的任务
  • SHUTDOWN:(关闭中)不再接收新的task,只处理已经进入队列的task,不会中断正在执行的任务
  • STOP:(停止)不接收新的task,也不再处理队列中等待的task,并且会中断正在执行中的任务
  • TIDYING:(整理中)所有task都已经终止,此时上述提到的workCount,也就是当前活跃线程数为0,随后会执行terminated()钩子方法
  • TERMINATED:(已终止)表示上述TIDYING阶段已经结束,说明当前线程池已经被完全关闭

状态转换

大致流程如下:

RUNNING --> SHUTDOWN --> STOP --> TIDYING --> TERMINATED

上述流程是单向顺序转换,也就是说不会出现类似STOP --> SHUTDOWN 这样的逆向变化;

另外,并不是每一种状态都一定会经历;

那么,线程池会在什么时候发生状态转换呢?

  • RUNNING -> SHUTDOWN:调用关闭线程池的方法shutdown()之后
  • RUNNING or SHUTDOWN -> STOP:调用shutdownNow()方法后,无论当前线程池处于RUNNING还是SHUTDOWN,都会直接切换到STOP状态
  • SHUTDOWN -> TIDYING:在SHUTDOWN状态下,当等待队列和正在执行的任务都为空时,状态切换为TIDYING
  • STOP -> TIDYING:在STOP状态下,当正在执行的任务全部中断完成后,状态切换为TIDYING
  • TIDYING -> TERMINATED:在TIDYING状态下,当所有terminated()钩子方法执行完成后,状态切换为TERMINATED,表示线程池关闭结束!

管理线程池状态

在线程池中,线程池状态和线程池当前活跃线程数,是通过一个AtomicInteger变量统一管理的

什么? 一个变量同时管理两个看起来并不相关的状态? 没错;

CTL变量何许人也

我们先来看一下线程池在这部分的源码实现:

    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; }
private static boolean isRunning(int c) { return c < SHUTDOWN;}

下面,我们来深入拆解一下上述实现: 线程池一共包含5种状态,如下所示;关于每种状态的具体含义以及状态之间的转换规则,前文已经介绍过:

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

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;

我们知道,在ja va中,int类型占用4个字节,也就是32位存储空间。上述几种状态在底层对应的二进制值分别为:

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)

左移<,其中COUNT_BITS = Integer.SIZE - 3,也就是 COUNT_BITS = 29。这句话的含义是:用32位中的前3位存储线程池状态,后29位存储线程池中当前工作线程的数量。经过<< COUNT_BITS后,就会变成下面的二进制:

1110 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 0000 0000 0000 0000 0000 0000 0100 0000 0000 0000 0000 0000 0000 0000 0110 0000 0000 0000 0000 0000 0000 0000

我们可以清楚地看到,前三位存储的就是用于标识线程池状态的二进制值

对于初始化这些状态的变量AtomicInteger ctl

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0))

这里初始化了AtomicInteger变量ctl,其中ctlOf(RUNNING, 0) 的代码如下:

private static int ctlOf(int rs, int wc) { return rs | wc; }

其中rs表示线程池当前状态,wc为work count,表示当前工作线程的数量

这里传入的是ctlOf(RUNNING, 0),当前状态为RUNNING,也就是1110 0000 0000 0000 0000 0000 0000 0000;wc为0,也就是当前工作线程数为0,其二进制为0000 0000 0000 0000 0000 0000 0000 0000。两者进行"|"按位或操作,即

1110 0000 0000 0000 0000 0000 0000 0000 | 0000 0000 0000 0000 0000 0000 0000 0000 = 1110 0000 0000 0000 0000 0000 0000 0000

上述得到的结果1110 0000 0000 0000 0000 0000 0000 0000就表示:当前线程池状态为RUNNING,线程池活跃线程数为0!

如何管理?

通过上面创建的ctl变量,我们就可以同时获取 线程池当前状态 和 线程池中活跃线程个数 这两个信息:

先看如何获取线程池当前状态。既然现在已经知道ctl的前3位表示线程池状态,那么直接构造一个前3位为1、后29位为0的int值,再进行按位与操作即可。源码中的实现也正是如此:使用方法runStateOf

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

其中CAPACITY = (1 << COUNT_BITS) - 1,转换成二进制为: 0001 1111 1111 1111 1111 1111 1111 1111。取反"~"之后,二进制为: 1110 0000 0000 0000 0000 0000 0000 0000,也就是将前3位全部变为1,后29位全部变为0; 接下来,传入的ctl变量与~CAPACITY做“&”操作,就只会保留ctl变量的前3位,后29位全部清零;

例如:一个表示当前状态为STOP、当前活跃线程数为3的ctl变量为: 0010 0000 0000 0000 0000 0000 0000 0011,和上面得到的1110 0000 0000 0000 0000 0000 0000 0000做“&”操作后得到: 0010 0000 0000 0000 0000 0000 0000 0000。这与前面分析出的STOP状态二进制完全一致! 也就意味着成功获得了当前线程池的状态!

获取线程池当前活跃线程数也很简单。因为ctl变量32位中的后29位存储的是当前活跃线程数,所以直接构造一个前3位为0、后29位为1的int值,然后进行按位与即可,这就是方法workerCountOf

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

前面已经知道,CAPACITY为:0001 1111 1111 1111 1111 1111 1111 1111

比如,一个表示当前状态为STOP、当前活跃线程数为3的ctl变量是:0010 0000 0000 0000 0000 0000 0000 0011,与 0001 1111 1111 1111 1111 1111 1111 1111 进行按位与操作后得到:0000 0000 0000 0000 0000 0000 0000 0011,这个结果就表示当前线程池中的活跃线程数量为3!

一些方法

1、计算ctl的值

方法:

private static int ctlOf(int rs, int wc) { return rs | wc; }

其中,入参rs表示当前线程池状态,wc表示当前活跃线程数,二者通过“|”按位或进行合并即可。上述代码能够正确工作的前提是:rs只使用前3位,wc只使用后29位!

2、判断当前线程池是否正在运行

方法:

private static boolean isRunning(int c) {  return c <小于SHUTDOWN;}值即可!

上面我们已经知道,5种状态里只有RUNNING小于0,SHUTDOWN等于0,其他状态都大于0,因此只要给定的ctl值小于SHUTDOWN,就说明线程池当前仍然处于运行状态!

最后

以上,我们介绍了线程池的状态管理部分。其核心思想就是通过一个整型变量中不同位置的二进制位,分别标识线程池状态和工作线程数量。这种设计既巧妙又高效,在学习和工作中还会遇到更多类似的优秀实现,等待着我们作为程序员去不断发现与理解;

来源:https://apiv1.oschina.net/oschinapi/blog/detail?id=4830404
上一篇前端JavaScript二叉树算法实现与应用详解 下一篇Java开发者如何快速玩转Serverless开发与落地
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
VMware安装Ubuntu完整教程:创建虚拟机与启动验证
系统平台 · 2026-09-01

VMware安装Ubuntu完整教程:创建虚拟机与启动验证

本教程详细演示如何在VMware中创建Ubuntu虚拟机,涵盖ISO挂载、硬件配置、安装向导及启动验证。通过清晰的步骤与验证命令,帮助新手快速搭建可用的Linux学习环境。

Win10专业版U盘安装教程:制作启动盘与完整安装步骤
系统平台 · 2026-09-01

Win10专业版U盘安装教程:制作启动盘与完整安装步骤

本文提供Win10专业版U盘安装完整流程:准备8GB以上U盘与官方镜像,制作启动盘并核对盘符;通过F12 F11 Esc等快捷键或BIOS设置U盘为第一启动项;安装时选择专业版并谨慎分区;完成后在“设置—系统—关于”验证版本与激活状态。操作前务必备份数据。

Windows10系统字体太小怎么调大
系统平台 · 2026-08-27

Windows10系统字体太小怎么调大

Windows10系统字体太小怎么调大?只需两步:首先打开设置中的显示选项,将缩放比例调整为125%或150%;随后运行ClearType文本调谐器优化字体清晰度。此方法适用于高分屏及普通屏幕,无需修改注册表即可解决界面拥挤问题。

Win10磁盘占用100%基础排查:从监控到清理的完整步骤
系统平台 · 2026-08-27

Win10磁盘占用100%基础排查:从监控到清理的完整步骤

Windows 10系统出现磁盘占用100%会导致电脑卡顿、程序响应缓慢。本文提供基础排查方案:首先通过任务管理器确认是否为磁盘高负载,随后进入系统存储页面分析C盘占用类别,最后针对性清理临时文件。遵循此流程可有效缓解磁盘压力,避免盲目重装系统。

Windows10系统怎么显示此电脑和控制面板
系统平台 · 2026-08-27

Windows10系统怎么显示此电脑和控制面板

Windows10默认可能不显示桌面图标,导致找不到“此电脑”和“控制面板”。只需进入个性化设置,在“桌面图标设置”中勾选对应选项即可恢复。本文提供详细图文步骤,帮助快速找回系统入口。