是不是直接开始到上图中标号为 ③ 的地方,去尝试添加非核心线程了? 也就是启用最大线程数这个配置了。 所以,朋友们,这个是什么情况? 这个情况确实就和我们背的线程池的八股文不一样了啊。 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());
- }
- }
复制代码等等: 如果你之前确实没了解过 Tomcat 线程池的工作机制,那么看到这里的时候也许你会觉得确实是有一点点收获。 但是,注意我要说但是了。 还记得最开始的时候面试官的问题吗? 面试官的原问题就是:一个 SpringBoot 项目能同时处理多少请求? 那么请问,前面我讲了这么大一坨 Tomcat 线程池运行原理,这个回答,和这个问题匹配吗? 是的,除了最开始提出的 200 这个数值之外,并不匹配,甚至在面试官的眼里完全是答非所问了。 所以,为了把这两个“并不匹配”的东西比较顺畅的链接起来,你必须要先回答面试官的问题,然后再开始扩展。 比如这样答:一个未进行任何特殊配置,全部采用默认设置的 SpringBoot 项目,这个项目同一时刻最多能同时处理多少请求,取决于我们使用的 web 容器,而 SpringBoot 默认使用的是 Tomcat。 Tomcat 的默认核心线程数是 10,最大线程数 200,队列长度是无限长。但是由于其运行机制和 JDK 线程池不一样,在核心线程数满了之后,会直接启用最大线程数。所以,在默认的配置下,同一时刻,可以处理 200 个请求。 在实际使用过程中,应该基于服务实际情况和服务器配置等相关消息,对该参数进行评估设置。 这个回答就算是差不多了。 但是,如果很不幸,如果你遇到了我,为了验证你是真的自己去摸索过,还是仅仅只是看了几篇文章,我可能还会追问一下: 那么其他什么都不动,如果我仅仅加入 server.tomcat.max-connections=10 这个配置呢,那么这个时候最多能处理多少个请求? 你可能就要猜了:10 个。 是的,我重新提交 1000 个任务过来,在控制台输出的确实是 10 个, 那么 max-connections 这个参数它怎么也能控制请求个数呢? 为什么在前面的分析过程中我们并没有注意到这个参数呢? 首先我们看一下它的默认值:
|