JDK11 线程池源码分析 (下)
六、这些面试问题会答了吗?
- Java线程池是如何保证核心线程不被销毁的?
- Java线程池中多余的线程是如何回收的?
- ThreadPoolExecutor中创建的线程如何被复用的?
- 线程池如何按照core、max、queue的执行循序去执行?
上面三个问题本质上是一个问题。由上图可知,线程池在执行任务时(运行 runWorker )会调用
getTask()
从阻塞队列中获取任务 ,核心线程调用的是BlockingQueue
的take()
,take()
会一直阻塞直到成功获取到阻塞队列中的任务,所以在阻塞期间核心线程时不会被销毁的。而非核心线程调用的是BlockingQueue
的poll()
,它最多只会等待`keepAliveTime
这么久。所以正常情况下,只要阻塞队列中有源源不断的任务,那么线程池中的线程就会被不断地复用。但是如果非核心线程在调用poll()
获取任务时超过了keepAliveTime
仍没有获取到就会返回null
,说明此时阻塞队列已经为空(这个线程目前来看是空闲的),所以当runWorker()
收到task
为null
时,就会调用processWorkerExit()
尝试去回收空闲的线程。具体来说,processWorkerExit()
会调用tryTerminate()
尝试去中断一个空闲的线程。如何判断当前线程可以被中断,就是通过调用worker
的独占锁lock()
来尝试去获取锁,获取成功说明当前worker
是空闲的,那么就中断wokrer
对应的线程。
4.线程池如何按照core、max、queue的执行循序去执行?
问题背景 :如果线程池执行的任务中,I/O密集型任务占大比例,假设一瞬间来了大量请求,当线程池中线程数大于
coreSize
时,多余的请求会被放到阻塞队列中,因为是IO密集型任务,所以此时极有可能CPU是空闲的,但是却有大量的任务在阻塞队列中,这种情况下CPU就未被充分利用。所以更好的办法应该是,当线程池中线程数大于coreSize
时,多余的请求通过非核心线程来执行,直到线程数量大于maxPoolSize
时,再将任务放到阻塞队列中。
解决办法 :修改workQueue.offer(command)
逻辑,在offer()
中增加一个判断,当线程数小于`maxPoolSize
,入队失败,返回false,这样就能先执行后面非核心线程(第三步)的逻辑,然后再执行第二步的逻辑。