共计 4046 个字符,预计需要花费 11 分钟才能阅读完成。
聊到线程池就肯定会聊到线程池的执行流程,也就是当有一个工作进入线程池之后,线程池是如何执行的?咱们明天就来聊聊这个话题。线程池是如何执行的?线程池的回绝策略有哪些?
线程池执行流程
想要真正的理解线程池的执行流程,就得先从线程池的执行办法 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); | |
} |
从上述源码咱们能够看出,当工作来了之后, 线程池的执行流程是:先判断以后线程数是否大于外围线程数?如果后果为 false,则新建线程并执行工作;如果后果为 true,则判断工作队列是否已满?如果后果为 false,则把工作增加到工作队列中期待线程执行,否则则判断以后线程数量是否超过最大线程数?如果后果为 false,则新建线程执行此工作,否则将执行线程池的回绝策略 ,如下图所示:
线程池回绝策略
当工作过多且线程池的工作队列已满时,此时就会执行线程池的回绝策略,线程池的回绝策略默认有以下 4 种:
- AbortPolicy:停止策略,线程池会抛出异样并中止执行此工作;
- CallerRunsPolicy:把工作交给增加此工作的(main)线程来执行;
- DiscardPolicy:疏忽此工作,疏忽最新的一个工作;
- DiscardOldestPolicy:疏忽最早的工作,最先退出队列的工作。
默认的回绝策略为 AbortPolicy 停止策略。
DiscardPolicy 回绝策略
接下来咱们以 DiscardPolicy 疏忽此工作,疏忽最新的一个工作为例,演示一下回绝策略的具体应用,实现代码如下:
public static void main(String[] args) { | |
// 工作的具体方法 | |
Runnable runnable = new Runnable() { | |
@Override | |
public void run() {System.out.println("当前任务被执行, 执行工夫:" + new Date() + | |
"执行线程:" + Thread.currentThread().getName()); | |
try { | |
// 期待 1s | |
TimeUnit.SECONDS.sleep(1); | |
} catch (InterruptedException e) {e.printStackTrace(); | |
} | |
} | |
}; | |
// 创立线程, 线程的工作队列的长度为 1 | |
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, | |
100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), | |
new ThreadPoolExecutor.DiscardPolicy()); | |
// 增加并执行 4 个工作 | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
// 线程池执行完工作,敞开线程池 | |
threadPool.shutdown();} |
以上程序的执行后果如下:
从上述执行后果能够看出,给线程池增加了 4 个工作,而线程池只执行了 2 个工作就完结了,其余两个工作执行了回绝策略 DiscardPolicy 被忽略了,这就是回绝策略的作用。
AbortPolicy 回绝策略
为了和 DiscardPolicy 回绝策略比照,咱们来演示一下 JDK 默认的回绝策略 AbortPolicy 停止策略,线程池会抛出异样并中止执行此工作,示例代码如下:
public static void main(String[] args) { | |
// 工作的具体方法 | |
Runnable runnable = new Runnable() { | |
@Override | |
public void run() {System.out.println("当前任务被执行, 执行工夫:" + new Date() + | |
"执行线程:" + Thread.currentThread().getName()); | |
try { | |
// 期待 1s | |
TimeUnit.SECONDS.sleep(1); | |
} catch (InterruptedException e) {e.printStackTrace(); | |
} | |
} | |
}; | |
// 创立线程, 线程的工作队列的长度为 1 | |
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, | |
100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), | |
new ThreadPoolExecutor.AbortPolicy()); // 显式指定回绝策略,也能够疏忽此设置,它为默认回绝策略 | |
// 增加并执行 4 个工作 | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
// 线程池执行完工作,敞开线程池 | |
threadPool.shutdown();} |
以上程序的执行后果如下:
从后果能够看出,给线程池增加了 4 个工作,线程池失常执行了 2 个工作,其余两个工作执行了停止策略,并抛出了拒绝执行的异样 RejectedExecutionException。
自定义回绝策略
当然除了 JDK 提供的四种回绝策略之外,咱们还能够实现通过 new RejectedExecutionHandler,并重写 rejectedExecution 办法来实现自定义回绝策略,实现代码如下:
public static void main(String[] args) { | |
// 工作的具体方法 | |
Runnable runnable = new Runnable() { | |
@Override | |
public void run() {System.out.println("当前任务被执行, 执行工夫:" + new Date() + | |
"执行线程:" + Thread.currentThread().getName()); | |
try { | |
// 期待 1s | |
TimeUnit.SECONDS.sleep(1); | |
} catch (InterruptedException e) {e.printStackTrace(); | |
} | |
} | |
}; | |
// 创立线程, 线程的工作队列的长度为 1 | |
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, | |
100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), | |
new RejectedExecutionHandler() { | |
@Override | |
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { | |
// 执行自定义回绝策略的相干操作 | |
System.out.println("我是自定义回绝策略~"); | |
} | |
}); | |
// 增加并执行 4 个工作 | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
threadPool.execute(runnable); | |
} |
以上程序的执行后果如下:
总结
线程池的执行流程有 3 个重要的判断点(判断程序顺次往后):判断以后线程数和外围线程数、判断当前任务队列是否已满、判断以后线程数是否已达到最大线程数。如果通过以上 3 个判断,失去的后果都会 true,则会执行线程池的回绝策略。JDK 提供了 4 种回绝策略,咱们还能够通过 new RejectedExecutionHandler 并重写 rejectedExecution 办法来实现自定义回绝策略。
是非审之于己,毁誉听之于人,得失安之于数。
公众号:Java 面试真题解析
面试合集:https://gitee.com/mydb/interview