六、这些面试问题会答了吗?

  1. Java线程池是如何保证核心线程不被销毁的?
  2. Java线程池中多余的线程是如何回收的?
  3. ThreadPoolExecutor中创建的线程如何被复用的?
  4. 线程池如何按照core、max、queue的执行循序去执行?

image.png

上面三个问题本质上是一个问题。由上图可知,线程池在执行任务时(运行 runWorker )会调用 getTask() 从阻塞队列中获取任务 ,核心线程调用的是 BlockingQueuetake()take() 会一直阻塞直到成功获取到阻塞队列中的任务,所以在阻塞期间核心线程时不会被销毁的。而非核心线程调用的是 BlockingQueuepoll() ,它最多只会等待`keepAliveTime这么久。所以正常情况下,只要阻塞队列中有源源不断的任务,那么线程池中的线程就会被不断地复用。但是如果非核心线程在调用poll() 获取任务时超过了 keepAliveTime 仍没有获取到就会返回 null,说明此时阻塞队列已经为空(这个线程目前来看是空闲的),所以当 runWorker() 收到 tasknull 时,就会调用 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,这样就能先执行后面非核心线程(第三步)的逻辑,然后再执行第二步的逻辑。

img