聊到线程池就一定会聊到线程池的执行流程, 也就是当有一个任务进入线程池之后, 线程池是如何执行的?
想要真正的了解线程池的执行流程,就得先从线程池的执行方法 execute() 说起, execute() 实现源码如下:
- public void execute(Runnable command) {
- if (command == null) {
- throw new NullPointerException();
- }
-
- int c = ctl.get();
-
- // 当前工作的线程数小于核心线程数
- if (workerCountOf(c) < corePoolSize) {
- // 创建新的线程执行此任务
- if (addWorker(command, true)) {
- return;
- }
- c = ctl.get();
- }
- // 检查线程池是否处于运行状态,如果是则把任务添加到队列
- if (isRunning(c) && workQueue.offer(command)) {
- int recheck = ctl.get();
-
- // 再次检线程池是否处于运行状态,防止在第一次校验通过后线程池关闭
- // 如果是非运行状态,则将刚加入队列的任务移除
- if (! isRunning(recheck) && remove(command)) {
- reject(command);
- }
-
- // 如果线程池的线程数为 0 时(当 corePoolSize 设置为 0 时会发生)
- else if (workerCountOf(recheck) == 0) {
- addWorker(null, false); // 新建线程执行任务
- }
- }
- // 核心线程都在忙且队列都已爆满,尝试新启动一个线程执行失败
- else if (!addWorker(command, false)) {
- // 执行拒绝策略
- reject(command);
- }
- }
线程池的执行流程有 3 个重要的判断点:
如果在经过上诉三个过程后, 得到的结果都是 true , 那么就会执行线程池的拒绝策略.
当任务过多且线程池的任务队列已满时, 此时就会执行线程池的拒绝策略, 线程池的拒绝策略默认有以下 4 种:
在分析JDK自带的线程池拒绝策略前,先看下JDK定义的 拒绝策略接口:
- public interface RejectedExecutionHandler {
- void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
- }
接口定义大概就是, 当触发拒绝策略时, 线程池会根据你设置的具体的策略, 将当前提交的任务以及线程池实例本身传递给你处理, 具体作何处理, 不同场景会有不同的考虑.
功能: 当触发拒绝策略时,直接抛出拒绝执行的异常,中止策略的意思也就是打断当前执行流程
功能:当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程处理.
功能:直接静悄悄的丢弃这个任务,不触发任何动作.
功能:如果线程池未关闭,就弹出队列头部的元素,然后尝试执行.