犀利豆的博客

犀利豆的博客

马上订阅 犀利豆的博客 RSS 更新: https://xilidou.com/atom.xml

线程池 execute() 的工作逻辑

2018年2月10日 07:42

原文地址:https://www.xilidou.com/2018/02/09/thread-corepoolsize/

最近在看《Java并发编程的艺术》回顾线程池的原理和参数的时候发现一个问题,如果 corePoolSize = 0 且 阻塞队列是无界的。线程池将如何工作?

我们先回顾一下书里面描述线程池execute()工作的逻辑:

  1. 如果当前运行的线程,少于corePoolSize,则创建一个新的线程来执行任务。
  2. 如果运行的线程等于或多于 corePoolSize,将任务加入 BlockingQueue。
  3. 如果 BlockingQueue 内的任务超过上限,则创建新的线程来处理任务。
  4. 如果创建的线程数是单钱运行的线程超出 maximumPoolSize,任务将被拒绝策略拒绝。

看了这四个步骤,其实描述上是有一个漏洞的。如果核心线程数是0,阻塞队列也是无界的,会怎样?如果按照上文的逻辑,应该没有线程会被运行,然后线程无限的增加到队列里面。然后呢?

于是我做了一下试验看看到底会怎样?

1
2
3
4
5
6
7
8
9
10
11
public class threadTest {
private final static ThreadPoolExecutor executor = new ThreadPoolExecutor(0,1,0, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger();
while (true) {
executor.execute(() -> {
System.out.println(atomicInteger.getAndAdd(1));
});
}
}
}

结果里面的System.out.println(atomicInteger.getAndAdd(1));语句执行了,与上面的描述矛盾了。到底发生了什么?线程池创建线程的逻辑是什么?我们还是从源码来看看到底线程池的逻辑是什么?

ctl

要了解线程池,我们首先要了解的线程池里面的状态控制的参数 ctl。

  • 线程池的ctl是一个原子的 AtomicInteger。
  • 这个ctl包含两个参数 :
    • workerCount 激活的线程数
    • runState 当前线程池的状态
  • 它的低29位用于存放当前的线程数, 因此一个线程池在理论上最大的线程数是 536870911; 高 3 位是用于表示当前线程池的状态, 其中高三位的值和状态对应如下:
    • 111: RUNNING
    • 000: SHUTDOWN
    • 001: STOP
    • 010: TIDYING
    • 110: TERMINATED

为了能够使用 ctl 线程池提供了三个方法:

1
2
3
4
5
6
7
// Packing and unpacking ctl
// 获取线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 获取线程池的工作线程数
private static int workerCountOf(int c) { return c & CAPACITY; }
// 根据工作线程数和线程池状态获取 ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }

execute

外界通过 execute 这个方法来向线程池提交任务。

先看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15...

剩余内容已隐藏

查看完整文章以阅读更多