预备知识

可以先看下我的另一篇文章对于Java中的位掩码BitMask的解释。

  • 1、一个整数在jvm中占用了4个字节,共32bits
  • 2、最高位的bit代表符号位,0为正数、1为负,剩余的31bits则代表数字部分
  • 3、反码加1即为补码
  • 4、对于负数而言,是以补码的形式存储在内存中的。以-7(int)为例
    • 1)、将-7的绝对值转化为二进制:

      0000 0000 0000 0000 0000 0000 0000 0111
    • 2):将上面的二进制以反码表示:

      1111 1111 1111 1111 1111 1111 1111 1000
    • 3):转化为补码:

      1111 1111 1111 1111 1111 1111 1111 1001

源码分析

我们把ThreadPoolExecutor中的状态和状态相关的方法复制出来,然后创建一个线程池,在运行中的时候分析线程池的状态和线程数,于是有了下面例子:

@Slf4j
public class ThreadPoolExecutorCtlAnalysis {
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;// 000,11111111111111111111111111111 // runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS; // 111,00000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS; // 000,00000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS; // 001,00000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS; // 010,00000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;// 011,00000000000000000000000000000 // Packing and unpacking ctl // RUNNING(3'thread) 111,00000000000000000000000000011
// ~CAPACITY 111,00000000000000000000000000000
// RESULT 111,00000000000000000000000000000
// 与操作取高位获取的就是ctl中保存的的线程池的状态
private static int runStateOf(int c) {
return c & ~CAPACITY;
} // RUNNING(3'thread) 111,00000000000000000000000000011
// CAPACITY 000,11111111111111111111111111111
// RESULT 000,00000000000000000000000000011
// 与操作取低位获取的就是ctl中保存的worker数量
private static int workerCountOf(int c) {
return c & CAPACITY;
} private static Runnable buildRunnableTask() {
return () -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Task finished.");
};
} private static int getCtlValue(ThreadPoolExecutor executor, Field field) {
//noinspection ConstantConditions
return ((AtomicInteger) ReflectionUtils.getField(field, executor)).get();
} private static String formatBinaryString(int state) {
StringBuilder binaryString = new StringBuilder(Integer.toBinaryString(state));
if (binaryString.length() < Integer.SIZE) {
for (int i = binaryString.length(); i < Integer.SIZE; i++) {
binaryString.insert(0, "0");
}
}
return binaryString.substring(0, 3) + "," + binaryString.substring(3, Integer.SIZE);
} private static void peekThreadPoolExecuteState(ThreadPoolExecutor executor, Field ctlField) {
log.info("------------------- ThreadPoolExecuteState -------------------");
int ctlValue = getCtlValue(executor, ctlField);
log.info("getCtlValue : {}", formatBinaryString(ctlValue));
log.info("workerCountOf: {}", workerCountOf(ctlValue));
log.info("Is RUNNING: {}", runStateOf(ctlValue) == RUNNING);
log.info("Is SHUTDOWN: {}", runStateOf(ctlValue) == SHUTDOWN);
log.info("Is STOP: {}", runStateOf(ctlValue) == STOP);
log.info("Is TIDYING: {}", runStateOf(ctlValue) == TIDYING);
log.info("Is TERMINATED: {}", runStateOf(ctlValue) == TERMINATED);
} public static void main(String[] args) throws NoSuchFieldException, InterruptedException {
// 打印出来看看几种状态的二进制表示
log.info("{} --> CAPACITY", formatBinaryString(CAPACITY));
log.info("{} --> RUNNING", formatBinaryString(RUNNING));
log.info("{} --> STOP", formatBinaryString(STOP));
log.info("{} --> TERMINATED", formatBinaryString(TERMINATED)); // 创建一个线程池,运行3个任务
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 2, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1));
executor.submit(buildRunnableTask());
executor.submit(buildRunnableTask());
executor.submit(buildRunnableTask());
// 休眠一秒钟,可以拿到中间状态的ctl
Thread.sleep(1000);
log.info("getActiveCount(): {}", executor.getActiveCount());
// 通过反射能拿到ThreadPoolExecutor的ctl的值
Field ctlField = ThreadPoolExecutor.class.getDeclaredField("ctl");
ctlField.setAccessible(true);
// 线程池运行中的状态可通过ctl拿到
peekThreadPoolExecuteState(executor, ctlField);
// 终止线程池,再来看看线程池中ctl的状态
executor.shutdownNow();
peekThreadPoolExecuteState(executor, ctlField);
// 休眠2秒钟,看看线程池最终的状态
Thread.sleep(2000);
peekThreadPoolExecuteState(executor, ctlField);
}
}

在看运行结果之前,我们先看下ThreadPoolExecutor中的几处涉及到状态变更的方法实现。

submit()源码分析

public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}

最终调用的是内部的execute方法:

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException(); int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}

这个方法不是特别复杂,我们本文的重点是要看看它的addWorker()方法,这个不复制太多逻辑,关键在两行:

private boolean addWorker(Runnable firstTask, boolean core) {
int c = ctl.get();
...
compareAndIncrementWorkerCount(c)
...
}
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}

这里控制的是ctl中工作线程数(wc:WorkerCount)的变更,即整形低29位的自增不会影响到高3位的状态:

RUNNING(0'wc) 111,00000000000000000000000000000
RUNNING(1'wc) 111,00000000000000000000000000001

所以可预见的输出结果就是:

workerCountOf(): 1
Is Running: true
Is Stop: false

注意的是这些值都从ctl属性中得来。

shutdownNow()源码分析

在我们的例子中,我们调用了shutdownNow()方法来改变线程池的状态。

public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}

这里我们关注的是advanceRunState(STOP)方法:

/**
* Transitions runState to given target, or leaves it alone if
* already at least the given target.
*
* @param targetState the desired state, either SHUTDOWN or STOP
* (but not TIDYING or TERMINATED -- use tryTerminate for that)
*/
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}

该方法最终是要把当前状态变为STOP状态。

注意点一:

ThreadPoolExecutor中状态定义的值大小是有序的,即:

TERMINATED > TIDYING > STOP > SHUTDOWN > RUNNING(最高位1是负数)

注意点二:

advance的含意是推进、前进的意思,Java并发包里的很多方法都使用了该命名,所以当前方法表示的意思是要推进运行状态(advanceRunState),因此方法中才有了runStateAtLeast()判断。

即要推进状态,那如果当前状态已经大于目标状态了,本次方法直接跳过。否则才去做cas操作。这也正是原方法注释想表达的意思。

注意点三:

最后在做cas操作的时候合并当前wc和rs的值,使用的是ctlOf方法:

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

通过与运算把RunState和WorkerCount的值合并到一处,即最终的ctl的值:

STOP      001,00000000000000000000000000000
WorkCount 000,00000000000000000000000000001
ctl value 001,00000000000000000000000000001

所以可预见的输出结果就是:

workerCountOf(): 1
Is Running: false
Is Stop: true

代码输出

12:56:19.473 [main] ThreadPoolExecutorCtlAnalysis - 000,11111111111111111111111111111 --> CAPACITY
12:56:19.476 [main] ThreadPoolExecutorCtlAnalysis - 111,00000000000000000000000000000 --> RUNNING
12:56:19.476 [main] ThreadPoolExecutorCtlAnalysis - 001,00000000000000000000000000000 --> STOP
12:56:19.476 [main] ThreadPoolExecutorCtlAnalysis - 011,00000000000000000000000000000 --> TERMINATED
12:56:20.520 [main] ThreadPoolExecutorCtlAnalysis - getActiveCount(): 2
12:56:20.520 [main] ThreadPoolExecutorCtlAnalysis - ------------------- ThreadPoolExecuteState -------------------
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - getCtlValue : 111,00000000000000000000000000010
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - workerCountOf: 2
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is RUNNING: true
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is SHUTDOWN: false
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is STOP: false
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is TIDYING: false
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is TERMINATED: false
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - ------------------- ThreadPoolExecuteState -------------------
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - getCtlValue : 001,00000000000000000000000000010
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - workerCountOf: 2
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is RUNNING: false
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is SHUTDOWN: false
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is STOP: true
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is TIDYING: false
12:56:20.533 [main] ThreadPoolExecutorCtlAnalysis - Is TERMINATED: false
12:56:20.534 [pool-1-thread-1] ThreadPoolExecutorCtlAnalysis - Task finished.
12:56:20.534 [pool-1-thread-2] ThreadPoolExecutorCtlAnalysis - Task finished.
12:56:22.538 [main] ThreadPoolExecutorCtlAnalysis - ------------------- ThreadPoolExecuteState -------------------
12:56:22.538 [main] ThreadPoolExecutorCtlAnalysis - getCtlValue : 011,00000000000000000000000000000
12:56:22.538 [main] ThreadPoolExecutorCtlAnalysis - workerCountOf: 0
12:56:22.539 [main] ThreadPoolExecutorCtlAnalysis - Is RUNNING: false
12:56:22.539 [main] ThreadPoolExecutorCtlAnalysis - Is SHUTDOWN: false
12:56:22.539 [main] ThreadPoolExecutorCtlAnalysis - Is STOP: false
12:56:22.539 [main] ThreadPoolExecutorCtlAnalysis - Is TIDYING: false
12:56:22.539 [main] ThreadPoolExecutorCtlAnalysis - Is TERMINATED: true

可以看到使用ctl一个字段可以获取到两个值,并且这两个值不会有并发不一致的情况,每次都是一次cas更新值。

设计目的与优点

线程池自身的状态和线程数量都维护在一个原子变量ctl中,目的不是为了减少存储空间,而是将线程池状态与线程个数合二为一,这样就可以用一次cas原子操作进行赋值,更容易保证在多线程环境下保证运行状态和线程数量的统一。这真是大师的设计智慧啊!

线程池中状态与线程数的设计分析(ThreadPoolExecutor中ctl变量)的更多相关文章

  1. Java-技术专区-如何监控Java线程池的状态

    线程池介绍 什么是线程池.线程池核心类.线程池工作流程.线程池分类.拒绝策略.及如何提交与关闭线程池等. 但在实际开发过程中,在线程池使用过程中可能会遇到各方面的故障,如线程池阻塞,无法提交新任务等. ...

  2. C# 线程池的使用 终止线程池中的队列

    C#的线程池使用起来还是非常简单的,这里记录一下. 根据http://blog.csdn.net/chen_zw/article/details/7939834里的描述这里记录一下C#线程池的特点 一 ...

  3. juc线程池原理(四): 线程池状态介绍

    <Thread之一:线程生命周期及五种状态> <juc线程池原理(四): 线程池状态介绍> 线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态.线程池也有5种状态 ...

  4. JavaSE_多线程入门 线程安全 死锁 状态 通讯 线程池

    1 多线程入门 1.1 多线程相关的概念 并发与并行 并行:在同一时刻,有多个任务在多个CPU上同时执行. 并发:在同一时刻,有多个任务在单个CPU上交替执行. 进程与线程 进程:就是操作系统中正在运 ...

  5. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  6. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  7. java多线程系类:JUC线程池:01之线程池架构

    概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...

  8. 使用Callable接口创建线程和使用线程池的方式创建线程

    1.使用Callable接口的方式实现多线程,这是JDK5.0新增的一种创建多线程的方法 package com.baozi.java2; import java.util.concurrent.Ca ...

  9. 线程池如何复用一个线程-- ThreadPoolExecutor的实现(未完)

    任务是一组逻辑工作单元,而线程则是使任务异步执行的机制.在Java中,Runnable对象代表一个任务,Thread对象负责创建一个线程执行这个任务. 前提:1. 程序需要处理大量任务 2. 任务的执 ...

随机推荐

  1. eclipse 开发常见问题集锦

    问题1: eclipse导入外部项目,中文显示乱码(如下图) 方案:项目名-->右键属性-->如下图: 问题2: jsp/html页面eclipse双击打开,代码在工作区不显示(如下图:) ...

  2. oracle 1day

    1.主流数据库: 2.项目选择数据库的原则: 3.oracle 常用用户sys (sysdba系统管理员),system(sysoper系统操作员),scott(密码tiger) sys login: ...

  3. Cocos2d-x extensions库使用问题解决方法

    需要在加入头文件#include "cocos-ext.h" 1>e:\cocos\cocos2d-x\cocos2d-x-3.10\extensions\gui\cccon ...

  4. Longest common subsequence(LCS)

    问题 说明该问题在生物学中的实际意义 Biological applications often need to compare the DNA of two (or more) different ...

  5. CodeForces 1419F Rain of Fire

    题意 不想写. 题解 场上想了 1h+ 无果,一到场外就口胡出来了,我真是个 sb. 首先注意到如果 \(t\) 满足条件那么 \(t+1\) 也会满足,所以答案具有单调性,可以二分,于是现在只需要考 ...

  6. go-zero 是如何追踪你的请求链路的

    go-zero 是如何追踪你的请求链路 微服务架构中,调用链可能很漫长,从 http 到 rpc ,又从 rpc 到 http .而开发者想了解每个环节的调用情况及性能,最佳方案就是 全链路跟踪. 追 ...

  7. Django之实现分页显示内容

    关注公众号"轻松学编程"了解更多.- ​ 分页 1.作用 数据加载优化 2.前端引入bootstrap样式: {# 引入bootstrap样式的cdn资源 #} <link ...

  8. 对于RBAC与shiro的一些思考

    一.什么是RBAC模型 RBAC模型是一个解决用户权限问题的设计思维. 在最简单的RBAC模型中,将用户表设计为如下几个表 1.用户 2.角色 3.权限 以及这三张表衍生出来的两张中间表 4.用户_角 ...

  9. mvc SelectList 给下拉框 @Html.DropDownList绑定值

    后台代码: public class DropController : Controller { // GET: Drop public ActionResult Index() { List< ...

  10. CentOS 8.x 下尝试安装.Net 5 的运行时

    1.背景 看着不管是群里还是公众号里这几天最热闹就是.Net 5.0 正式版的发布.C#9. 当然要开发.net 5.0 的项目就需要把VisualStudio升级的v16.8.0版本了.升级后自带着 ...