线程池
https://mp.weixin.qq.com/s/o4pq9dCYSGc9NJXMAUNGxA
问题:线程池 coolsize设置为 0,会出现什么情况?



以下是 JDK 21 线程池类 ThreadPoolExecutor#execute 方法源码:
public void execute(Runnable command) { // 检查传入的任务是否为空,如果为空则抛出 NullPointerException if (command == null) thrownew NullPointerException(); // 获取当前线程池的控制状态 int c = ctl.get(); // 步骤 1: 如果当前运行的线程数少于核心线程数 if (workerCountOf(c) < corePoolSize) { // 尝试添加一个新的工作线程来执行提交的任务 // 如果添加成功,则直接返回 if (addWorker(command, true)) return; // 再次获取线程池的控制状态,以应对并发变化 c = ctl.get(); } // 步骤 2 // 步骤 2.1: 如果线程池处于运行状态并且任务能够成功加入队列 if (isRunning(c) && workQueue.offer(command)) { // 再次检查线程池的状态,确认线程池仍然处于运行状态 int recheck = ctl.get(); // 2.2 如果线程池不再运行,并且任务能够从队列中移除,则拒绝任务 if (!isRunning(recheck) && remove(command)) reject(command); // 2.3 如果当前线程池没有运行的线程 elseif (workerCountOf(recheck) == 0) // 尝试添加一个新的非核心工作线程(false 表示非核心线程) addWorker(null, false); } // 步骤 3 else { // 3.1 尝试添加一个新的非核心工作线程来执行任务 if (!addWorker(command, false)) // 3.2 如果添加失败,说明线程池已关闭或达到饱和状态,因此拒绝任务 reject(command); } }
从源码可以看到,如果往线程池提交任务的时候,当 corePoolSize = 0 时,代码正常情况下会执行到步骤2。
以下三步是关键步骤:
步骤 2.1:
此时,如果线程池处于运行状态,并且任务能够成功加入队列,说明线程池不为空,线程正常执行任务。
步骤 2.3:
此时,如果当前线程池没有运行的线程,则尝试添加一个新的非核心工作线程,即任务会先进入队列排队再由线程获取任务执行。
步骤 3.1:
此时,说明队列满了无法加入任务,尝试添加一个新的非核心工作线程来执行任务,如果添加失败,说明线程池已关闭或达到饱和状态,因此拒绝任务。
扩展知识点:
这个逻辑在 JDK 6 之前略有不同,在 JDK 6 之前,当 corePoolSize = 0 的时候,先将这个任务放到阻塞队列中,只有等队列满了才创建线程来执行,而 JDK 6+ 是直接创建一个非核心线程,再放在队列中来执行,很显示,JDK 6 这个优化动作减小了内存溢出的可能性。
光说 JDK 6 和 JDK 8 这两个版本,对线程池的重构就很大,现在主流的版本都是 JDK 8+,这个了解一下就好。

浙公网安备 33010602011771号