共计 2292 个字符,预计需要花费 6 分钟才能阅读完成。
明天加入了一场电话面试,聊到了线程池,后果 …
问:你先自我介绍下吧
我:
问:线程池参数
我:(这个我会)巴拉巴拉
问:提交工作的过程
我:(窃喜,还好筹备过)巴拉巴拉
问:线程池状态
我:巴拉 …(卧槽,答复到线程状态下来了)
问:线程池怎么辨别外围线程和非核心线程
我:
最初一个问题,我说线程池不辨别外围和非核心,只是判断线程数量来决定是否销毁线程,后果对面讥嘲我没看过源码。是可忍孰不可忍,连忙看一波源码写文章沉着沉着。
线程池构造方法
线程池的构造方法,这些参数我想大家都会吧。外围线程数,最大线程数,存活工夫,工夫单位,阻塞队列,线程工厂,回绝策略。
线程池的 ctl 原子变量
ctl 是什么呢?ctl 创立源码如下,是原子整型。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
下面代码外面呈现的 RUNNING 又是什么呢?RUNNING 代码如下:
private static final int RUNNING = -1 << COUNT_BITS;
- 1 做位运算左移 COUNT_BITS 位,COUNT_BITS 又是啥呢?
private static final int COUNT_BITS = Integer.SIZE - 3;
@Native public static final int SIZE = 32;
COUNT_BITS 为 32-3=29,
- 1 的二进制示意为:11111111111111111111111111111111,
所以 RUNNING 的二进制示意为:11100000000000000000000000000000
那么 ctlOf(RUNNING, 0)是什么意思呢?ctlof 办法源码如下:
private static int ctlOf(int rs, int wc) {return rs | wc;}
ctl 的计算是:线程运行状态和工作线程数做或运算。
所以运行状态左移 29 位,就是为了把低 29 位让给工作线程数,本人只占据高三位。为什么只须要高三位呢?因为线程的状态只有 5 种,三位能示意 8 种状态,二位能示意 4 种,所以选三位。
// 线程池状态全副都左移 COUNT_BITS 29 位
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;
线程池还提供了 ctl 的拆解办法和拆解常量
// 拆解办法
private static int runStateOf(int c) {return c & ~CAPACITY;}
private static int workerCountOf(int c) {return c & CAPACITY;}
// 拆解常量
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
拆解常量为什么是(1 << COUNT_BITS) – 1 呢?
1 的二进制是 00000000000000000000000000000001
COUNT_BITS 上文咱们晓得 29 位
1 << 29 二进制是 00100000000000000000000000000000
1 << 29 - 1 的二进制是 00011111111111111111111111111111
clt & CAPACITY 获取到的就是舍弃高 3 位之后的值
clt & ~CAPACITY 获取到的就是舍弃低 29 位之后的值
线程池的 execute 办法
1356 行:判断线程数是否小于外围数
1357 行:小于外围数则新增一个 worker
1359 行:1357 行新增失败的状况下从新获取 ctl 的值
1361 行:判断线程池工作状态,如果是运行态则把工作提交到 workQueue 阻塞队列
1363 行:再次判断线程池工作状态,如果不是运行态则把工作从 workQueue 删除且执行回绝策略
1365 行:在 1363 行没有执行回绝策略的根底上,如果工作线程数是零,创立一个工作线程
1368 行:在 1361 行工作退出阻塞队列满的状况下,新增一个 worker
线程池的 addWorker 办法
addWorker 办法有点长,我收起了局部代码,否则图片太长了,不不便看。
895 行:retry:地位标记,用于 break,controller 跳转用
596 行:外层死循环,907 行是内层的死循环
901 行:判断线程池运行状态,如果不是运行状态,则退出办法
907 行:内层 for 循环
909 行:判断工作线程数是否大于 29 位或者大于外围线程数和非核心线程数
912 行:cas 减少工作线程数大小
913 行:cas 胜利退出最外层死循环
915 行:从新判断线程运行状态是否扭转
916 行:扭转的状况下从新执行外层循环,否则间接内层循环
925 行:创立一个 worker 对象
929 行:上锁
936 行:判断线程池运行状态
938 行:判断 worker 外面的线程是否曾经运行,曾经在运行则抛出异样
940 行:把创立的 worker 退出队列内存
947 行:开释锁
949 行:如何 worker 创立胜利,启动 worker 外面的线程
956 行:worker 创立失败,回滚局部信息
973 行:删除 workers 外面缓存下来的 worker
974 行:cas 使 worker 计数减一
975 行:尝试敞开线程池
明天先到这
我就看了下源码输入下文章,就花了几个小时,累死我了,明天先溜了