如果 workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
如果 workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满,则根据拒绝策略来处理该任务,默认的处理方式是直接抛异常。
执行流程图如下:
2.3.2 待执行任务的队列
待执行任务的队列是线程池能够管理任务的核心部分。线程池的本质是对任务和线程的管理,而做到这一点最关键的思想就是将任务和线程两者解耦,不让两者直接关联,才可以做后续的分配工作。线程池中是以生产者消费者模式,通过一个阻塞队列来实现的。阻塞队列缓存任务,工作线程从阻塞队列中获取任务。
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。
这两个附加的操作是:
在队列为空时,获取元素的线程会等待队列变为非空。
当队列满时,存储元素的线程会等待队列可用。
阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
下图中展示了 Thread1 往阻塞队列中添加元素,而线程 Thread2 从阻塞队列中移除元素:
使用不同的队列可以实现不一样的任务存取策略。我们下面来看下阻塞队列的成员:
2.3.3 任务申请
从上文可知,任务的执行有两种可能:
一种是任务直接由新创建的线程执行
另一种是线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行。
第一种情况仅出现在线程初始创建的时候,第二种是线程获取任务绝大多数的情况。
线程需要从待执行任务的队列中不断地取任务执行,帮助线程从阻塞队列中获取任务,实现线程管理模块和任务管理模块之间的通信。
这部分策略由 getTask 方法实现,我们来看下 getTask 方法的代码。
getTask 方法在阻塞队列中有待执行的任务时会从队列中弹出一个任务并返回,如果阻塞队列为空,那么就会阻塞等待新的任务提交到队列中直到超时(在一些配置下会一直等待而不超时),如果在超时之前获取到了新的任务,那么就会将这个任务作为返回值返回。所以一般 getTask 方法是不会返回 null 的,只会阻塞等待下一个任务并在之后将这个新任务作为返回值返回。
当 getTask 方法返回 null 时会导致当前 Worker 退出,当前线程被销毁。在以下情况下 getTask 方法才会返回 null:
当前线程池中的线程数超过了最大线程数。这是因为运行时通过调用 setMaximumPoolSize 修改了最大线程数而导致的结果;
线程池处于 STOP 状态。这种情况下所有线程都应该被立即回收销毁;
线程池处于 SHUTDOWN 状