面试题:线程池里究竟有多少线程在默默运行?
摘要:本文主要改编自文献1,最大改进是以dubbo EagerThreadPoolExecutor源码分析其实现机制。下面从一道面试题引入本文主题~~ 面试官:"假设有一个空的线程池,配置的核心线程数为10,最大线程数为
本文主要改编自文献1,最大改进是以dubbo EagerThreadPoolExecutor源码分析其实现机制。下面从一道面试题引入本文主题~~
面试官:"假设有一个空的线程池,配置的核心线程数为10,最大线程数为20,任务队列长度为100。如果现在来了100个任务,那么线程池里有几个线程在运行?"
粉丝豪:"应该是10吧!"
面试官:"你确定?"
粉丝豪:"确定啊!就是10…"
于是乎,漂亮的HR小姐姐让粉丝豪回去等通知了~
大家如果看出来了此题的陷阱,就不用看本文了!其实,这道题正确的答案是"不一定!"因为并没指明是哪一种线程池机制,带着这个疑问继续往下看!我们基于jdk 8,以两类线程池机制——先放队列再创建线程和先创建线程再放入队列——来剖析这道面试题。
先放队列再创建线程
针对线程数为0的空线程池,来了任务之后,先创建核心线程,核心线程数用完后,新来的任务先进队列,在队列满的时候,再创建线程。这种情况是大家最容易想到的情况,因为JDK中的线程池,也就是ThreadPoolExecutor就是这种机制!OK,我们先来看一下ThreadPoolExecutor的void execute(Runnable command)方法源码,如下图所示:
ThreadPoolExecutor中函数execute(Runnable command)源码
在int c = ctl.get()代码上方,折叠了如下所示的一段英文注释,解释了上述截图中的三步流程:
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
如果对英文不感冒,请参考下面的中文翻译:
判断当前活跃线程数是否小于corePoolSize,如果小于,则调用addWorker创建线程执行任务;
如果不小于corePoolSize,则将任务添加到workQueue队列;
如果放入workQueue失败,则创建线程执行任务,如果这时创建线程失败(当前线程数不小于maximumPoolSize时),就会调用函数reject拒绝接受任务。
用一张流程图来解释,如下:
ThreadPoolExecutor中函数execute(Runnable command)源码
如图所示,默认的机制为线程池里的核心线程数不够了,后面进来的任务会先丢队列,当队列满了,才起新线程。
