而且基于 maximumPoolSize 这个参数你往前翻代码会发现这个默认值就是 200好到这里你发现你之前猜测的“Tomcat 默认核心线程数是 200”是不对的。但是你一点也不慌再次结合你背的滚瓜烂熟的、非常扎实的线程池知识。并在心里又默念了一次当线程池接受到任务之后先启用核心线程数再使用队列长度最后启用最大线程数。因为我们前面验证了Tomcat 可以同时间处理 200 个请求而它的线程池核心线程数只有 10最大线程数是 200。这说明我前面这个测试用例把队列给塞满了从而导致 Tomcat 线程池启用了最大线程数嗯一定是这样的那么现在的关键问题就是Tomcat 线程池默认的队列长度是多少呢在当前的这个 Debug 模式下队列长度可以通过 AltF8 进行查看wc这个值是 Integer.MAX_VALUE这么大我一共也才 1000 个任务不可能被占满啊一个线程池核心线程数值为 10。最大线程数值为 200。队列长度值为 Integer.MAX_VALUE。1000 个比较耗时的任务过来之后应该是只有 10 个线程在工作然后剩下的 990 个进队列才对啊难道我八股文背错了这个时候不要慌嗦根辣条冷静一下。目前已知的是核心线程数值为 10。这 10 个线程的工作流程是符合我们认知的。但是第 11 个任务过来的时候本应该进入队列去排队。现在看起来是直接启用最大线程数了。所以我们先把测试用例修改一下那么问题就来了最后一个请求到底是怎么提交到线程池里面的前面说了Tomcat 的线程池源码和 JDK 的基本一样。往线程池里面提交任务的时候会执行 execute 这个方法org.apache.Tomcat.util.threads.ThreadPoolExecutor#execute(java.lang.Runnable)对于 Tomcat 它会调用到 executeInternal 这个方法org.apache.Tomcat.util.threads.ThreadPoolExecutor#executeInternal这个方法里面标号为 ① 的地方就是判断当前工作线程数是否小于核心线程数小于则直接调用 addWorker 方法创建线程。标号为 ② 的地方主要是调用了 offer 方法看看队列里面是否还能继续添加任务。如果不能继续添加说明队列满了则来到标号为 ③ 的地方看看是否能执行 addWorker 方法创建非核心线程即启用最大线程数。把这个逻辑捋顺之后接下来我们应该去看哪部分的代码就很清晰了。主要就是去看 workQueue.offer(command) 这个逻辑。如果返回 true 则表示加入到队列返回 false 则表示启用最大线程数嘛。这个 workQueue 是 TaskQueue看起来一点也不眼熟当然不眼熟了因为这个是 Tomcat 自己基于 LinkedBlockingQueue 搞的一个队列。问题的答案就藏在 TaskQueue 的 offer 方法里面。所以我重点带你盘一下这个 offer 方法org.apache.Tomcat.util.threads.TaskQueue#offer标号为 ① 的地方判断了 parent 是否为 null如果是则直接调用父类的 offer 方法。说明要启用这个逻辑我们的 parent 不能为 null。那么这个 parent 是什么玩意从哪里来的呢parent 就是 Tomcat 线程池通过其 set 方法可以知道是在线程池完成初始化之后进行了赋值。也就是说你可以理解为在 Tomcat 的场景下parent 不会为空。标号为 ② 的地方调用了 getPoolSizeNoLock 方法这个方法是获取当前线程池中有多个线程。所以如果这个表达式为 trueparent.getPoolSizeNoLock() parent.getMaximumPoolSize()就表明当前线程池的线程数已经是配置的最大线程数了那就调用 offer 方法把当前请求放到到队列里面去。标号为 ③ 的地方是判断已经提交到线程池里面待执行或者正在执行的任务个数是否比当前线程池的线程数还少。如果是则说明当前线程池有空闲线程可以执行任务则把任务放到队列里面去就会被空闲线程给取走执行。然后关键的来了标号为 ④ 的地方。如果当前线程池的线程数比线程池配置的最大线程数还少则返回 false。前面说了offer 方法返回 false会出现什么情况是不是直接开始到上图中标号为 ③ 的地方去尝试添加非核心线程了也就是启用最大线程数这个配置了。所以朋友们这个是什么情况这个情况确实就和我们背的线程池的八股文不一样了啊。JDK 的线程池是先使用核心线程数配置接着使用队列长度最后再使用最大线程配置。Tomcat 的线程池就是先使用核心线程数配置再使用最大线程配置最后才使用队列长度。所以以后当面试官给你说我们聊聊线程池的工作机制吧你就先追问一句你是说的 JDK 的线程池呢还是 Tomcat 的线程池呢因为这两个在运行机制上有一点差异。然后你就看他的表情。如果透露出一丝丝迟疑然后轻描淡写的说一句那就对比着说一下吧。那么恭喜你在这个题目上开始掌握了一点主动权。最后为了让你更加深刻的理解到 Tomcat 线程池和 JDK 线程池的不一样我给你搞一个直接复制过去就能运行的代码。当你把 taskqueue.setParent(executor) 这行代码注释掉的时候它的运行机制就是 JDK 的线程池。当存在这行代码的时候它的运行机制就变成了 Tomcat 的线程池。玩去吧。import org.apache.tomcat.util.threads.TaskQueue; import org.apache.tomcat.util.threads.TaskThreadFactory; import org.apache.tomcat.util.threads.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class TomcatThreadPoolExecutorTest { public static void main(String[] args) throws InterruptedException { String namePrefix 歪歪歪-exec-; boolean daemon true; TaskQueue taskqueue new TaskQueue(300); TaskThreadFactory tf new TaskThreadFactory(namePrefix, daemon, Thread.NORM_PRIORITY); ThreadPoolExecutor executor new ThreadPoolExecutor(5, 150, 60000, TimeUnit.MILLISECONDS, taskqueue, tf); taskqueue.setParent(executor); for (int i 0; i 300; i) { try { executor.execute(() - { logStatus(executor, 创建任务); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } Thread.currentThread().join(); } private static void logStatus(ThreadPoolExecutor executor, String name) { TaskQueue queue (TaskQueue) executor.getQueue(); System.out.println(Thread.currentThread().getName() - name -: 核心线程数: executor.getCorePoolSize() \t活动线程数: executor.getActiveCount() \t最大线程数: executor.getMaximumPoolSize() \t总任务数: executor.getTaskCount() \t当前排队线程数: queue.size() \t队列剩余大小: queue.remainingCapacity()); }
maximumPoolSize,最大线程数,值为 200。
发布时间:2026/7/2 5:57:41
而且基于 maximumPoolSize 这个参数你往前翻代码会发现这个默认值就是 200好到这里你发现你之前猜测的“Tomcat 默认核心线程数是 200”是不对的。但是你一点也不慌再次结合你背的滚瓜烂熟的、非常扎实的线程池知识。并在心里又默念了一次当线程池接受到任务之后先启用核心线程数再使用队列长度最后启用最大线程数。因为我们前面验证了Tomcat 可以同时间处理 200 个请求而它的线程池核心线程数只有 10最大线程数是 200。这说明我前面这个测试用例把队列给塞满了从而导致 Tomcat 线程池启用了最大线程数嗯一定是这样的那么现在的关键问题就是Tomcat 线程池默认的队列长度是多少呢在当前的这个 Debug 模式下队列长度可以通过 AltF8 进行查看wc这个值是 Integer.MAX_VALUE这么大我一共也才 1000 个任务不可能被占满啊一个线程池核心线程数值为 10。最大线程数值为 200。队列长度值为 Integer.MAX_VALUE。1000 个比较耗时的任务过来之后应该是只有 10 个线程在工作然后剩下的 990 个进队列才对啊难道我八股文背错了这个时候不要慌嗦根辣条冷静一下。目前已知的是核心线程数值为 10。这 10 个线程的工作流程是符合我们认知的。但是第 11 个任务过来的时候本应该进入队列去排队。现在看起来是直接启用最大线程数了。所以我们先把测试用例修改一下那么问题就来了最后一个请求到底是怎么提交到线程池里面的前面说了Tomcat 的线程池源码和 JDK 的基本一样。往线程池里面提交任务的时候会执行 execute 这个方法org.apache.Tomcat.util.threads.ThreadPoolExecutor#execute(java.lang.Runnable)对于 Tomcat 它会调用到 executeInternal 这个方法org.apache.Tomcat.util.threads.ThreadPoolExecutor#executeInternal这个方法里面标号为 ① 的地方就是判断当前工作线程数是否小于核心线程数小于则直接调用 addWorker 方法创建线程。标号为 ② 的地方主要是调用了 offer 方法看看队列里面是否还能继续添加任务。如果不能继续添加说明队列满了则来到标号为 ③ 的地方看看是否能执行 addWorker 方法创建非核心线程即启用最大线程数。把这个逻辑捋顺之后接下来我们应该去看哪部分的代码就很清晰了。主要就是去看 workQueue.offer(command) 这个逻辑。如果返回 true 则表示加入到队列返回 false 则表示启用最大线程数嘛。这个 workQueue 是 TaskQueue看起来一点也不眼熟当然不眼熟了因为这个是 Tomcat 自己基于 LinkedBlockingQueue 搞的一个队列。问题的答案就藏在 TaskQueue 的 offer 方法里面。所以我重点带你盘一下这个 offer 方法org.apache.Tomcat.util.threads.TaskQueue#offer标号为 ① 的地方判断了 parent 是否为 null如果是则直接调用父类的 offer 方法。说明要启用这个逻辑我们的 parent 不能为 null。那么这个 parent 是什么玩意从哪里来的呢parent 就是 Tomcat 线程池通过其 set 方法可以知道是在线程池完成初始化之后进行了赋值。也就是说你可以理解为在 Tomcat 的场景下parent 不会为空。标号为 ② 的地方调用了 getPoolSizeNoLock 方法这个方法是获取当前线程池中有多个线程。所以如果这个表达式为 trueparent.getPoolSizeNoLock() parent.getMaximumPoolSize()就表明当前线程池的线程数已经是配置的最大线程数了那就调用 offer 方法把当前请求放到到队列里面去。标号为 ③ 的地方是判断已经提交到线程池里面待执行或者正在执行的任务个数是否比当前线程池的线程数还少。如果是则说明当前线程池有空闲线程可以执行任务则把任务放到队列里面去就会被空闲线程给取走执行。然后关键的来了标号为 ④ 的地方。如果当前线程池的线程数比线程池配置的最大线程数还少则返回 false。前面说了offer 方法返回 false会出现什么情况是不是直接开始到上图中标号为 ③ 的地方去尝试添加非核心线程了也就是启用最大线程数这个配置了。所以朋友们这个是什么情况这个情况确实就和我们背的线程池的八股文不一样了啊。JDK 的线程池是先使用核心线程数配置接着使用队列长度最后再使用最大线程配置。Tomcat 的线程池就是先使用核心线程数配置再使用最大线程配置最后才使用队列长度。所以以后当面试官给你说我们聊聊线程池的工作机制吧你就先追问一句你是说的 JDK 的线程池呢还是 Tomcat 的线程池呢因为这两个在运行机制上有一点差异。然后你就看他的表情。如果透露出一丝丝迟疑然后轻描淡写的说一句那就对比着说一下吧。那么恭喜你在这个题目上开始掌握了一点主动权。最后为了让你更加深刻的理解到 Tomcat 线程池和 JDK 线程池的不一样我给你搞一个直接复制过去就能运行的代码。当你把 taskqueue.setParent(executor) 这行代码注释掉的时候它的运行机制就是 JDK 的线程池。当存在这行代码的时候它的运行机制就变成了 Tomcat 的线程池。玩去吧。import org.apache.tomcat.util.threads.TaskQueue; import org.apache.tomcat.util.threads.TaskThreadFactory; import org.apache.tomcat.util.threads.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class TomcatThreadPoolExecutorTest { public static void main(String[] args) throws InterruptedException { String namePrefix 歪歪歪-exec-; boolean daemon true; TaskQueue taskqueue new TaskQueue(300); TaskThreadFactory tf new TaskThreadFactory(namePrefix, daemon, Thread.NORM_PRIORITY); ThreadPoolExecutor executor new ThreadPoolExecutor(5, 150, 60000, TimeUnit.MILLISECONDS, taskqueue, tf); taskqueue.setParent(executor); for (int i 0; i 300; i) { try { executor.execute(() - { logStatus(executor, 创建任务); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } Thread.currentThread().join(); } private static void logStatus(ThreadPoolExecutor executor, String name) { TaskQueue queue (TaskQueue) executor.getQueue(); System.out.println(Thread.currentThread().getName() - name -: 核心线程数: executor.getCorePoolSize() \t活动线程数: executor.getActiveCount() \t最大线程数: executor.getMaximumPoolSize() \t总任务数: executor.getTaskCount() \t当前排队线程数: queue.size() \t队列剩余大小: queue.remainingCapacity()); }