线程池是如何执行的?拒绝策略有哪些?

5464b54ce4a7f097d7ea45479bab23bc.png

作者 | 磊哥

来源 | Java面试真题解析(ID:aimianshi666)

转载请联系授权(微信ID:GG_Stone)

聊到线程池就一定会聊到线程池的执行流程,也就是当有一个任务进入线程池之后,线程池是如何执行的?我们今天就来聊聊这个话题。线程池是如何执行的?线程池的拒绝策略有哪些?

线程池执行流程

想要真正的了解线程池的执行流程,就得先从线程池的执行方法 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,则新建线程执行此任务,否则将执行线程池的拒绝策略,如下图所示:1e73187c53e3d3bbc2eb854f65aee6f3.png

线程池拒绝策略

当任务过多且线程池的任务队列已满时,此时就会执行线程池的拒绝策略,线程池的拒绝策略默认有以下 4 种:

  1. AbortPolicy:中止策略,线程池会抛出异常并中止执行此任务;

  2. CallerRunsPolicy:把任务交给添加此任务的(main)线程来执行;

  3. DiscardPolicy:忽略此任务,忽略最新的一个任务;

  4. DiscardOldestPolicy:忽略最早的任务,最先加入队列的任务。

默认的拒绝策略为 AbortPolicy 中止策略。

DiscardPolicy拒绝策略

接下来我们以 DiscardPolicy 忽略此任务,忽略最新的一个任务为例,演示一下拒绝策略的具体使用,实现代码如下:

public static void main(String[] args) {// 任务的具体方法Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("当前任务被执行,执行时间:" + new Date() +" 执行线程:" + Thread.currentThread().getName());try {// 等待 1sTimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}};// 创建线程,线程的任务队列的长度为 1ThreadPoolExecutor 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();
}

以上程序的执行结果如下:02f5d6ab51feb8bfbaed5767dd0be368.png从上述执行结果可以看出,给线程池添加了 4 个任务,而线程池只执行了 2 个任务就结束了,其他两个任务执行了拒绝策略 DiscardPolicy 被忽略了,这就是拒绝策略的作用。

AbortPolicy拒绝策略

为了和 DiscardPolicy 拒绝策略对比,我们来演示一下 JDK 默认的拒绝策略 AbortPolicy 中止策略,线程池会抛出异常并中止执行此任务,示例代码如下:

public static void main(String[] args) {// 任务的具体方法Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("当前任务被执行,执行时间:" + new Date() +" 执行线程:" + Thread.currentThread().getName());try {// 等待 1sTimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}};// 创建线程,线程的任务队列的长度为 1ThreadPoolExecutor 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();
}

以上程序的执行结果如下:8aeea3023b79e23653d06e28ffac0e37.png从结果可以看出,给线程池添加了 4 个任务,线程池正常执行了 2 个任务,其他两个任务执行了中止策略,并抛出了拒绝执行的异常 RejectedExecutionException。

自定义拒绝策略

当然除了 JDK 提供的四种拒绝策略之外,我们还可以实现通过 new RejectedExecutionHandler,并重写 rejectedExecution 方法来实现自定义拒绝策略,实现代码如下:

public static void main(String[] args) {// 任务的具体方法Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("当前任务被执行,执行时间:" + new Date() +" 执行线程:" + Thread.currentThread().getName());try {// 等待 1sTimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}};// 创建线程,线程的任务队列的长度为 1ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// 执行自定义拒绝策略的相关操作System.out.println("我是自定义拒绝策略~");}});// 添加并执行 4 个任务threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);
}

以上程序的执行结果如下:b57e54b1f04aa8eca0f2a0371d9cf2a0.png

总结

线程池的执行流程有 3 个重要的判断点(判断顺序依次往后):判断当前线程数和核心线程数、判断当前任务队列是否已满、判断当前线程数是否已达到最大线程数。如果经过以上 3 个判断,得到的结果都会 true,则会执行线程池的拒绝策略。JDK 提供了 4 种拒绝策略,我们还可以通过 new RejectedExecutionHandler 并重写 rejectedExecution 方法来实现自定义拒绝策略。

是非审之于己,毁誉听之于人,得失安之于数。

公众号:Java面试真题解析

面试合集:https://gitee.com/mydb/interview

99406872883dfc817429be8e16df5763.gif

往期推荐

3f0b37ed8f251f400a4db22325ff1598.png

面试突击29:说一下线程池7个参数的含义?


6477fd8ff228e617febaa19dc8d7c3f1.png

面试突击28:线程池有几种创建方式?推荐使用哪种?


627bb71b57c7a3bd7380697a701ca096.png

汇总 | 2022版Java最新面试题(共29篇)


3dffba2b45cfd61c453bc21cf3f6589d.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浮动元素的均匀分布和两端对齐

当我们使用float来使元素并排显示的时候&#xff0c;可以使用margin来控制元素之间的距离&#xff0c;而在很多版式里&#xff08;例如产品图片的列表&#xff09;&#xff0c;需要浮动的元素达到两端对齐的效果&#xff0c;如图1所示。 图1 两端对齐的版式 单纯使用float:left…

20 图|Nacos 手摸手教程

Nacos 作为服务注册中心、配置中心&#xff0c;已经非常成熟了&#xff0c;业界的标杆&#xff0c;在讲解 Nacos 的架构原理之前&#xff0c;我先给大家来一篇开胃菜&#xff1a;讲解 Nacos 如何使用。涉及到如下两个话题&#xff1a;用 Nacos 作为注册中心。用 Nacos 作为配置…

为什么Spring需要三级缓存解决循环依赖,而不是二级缓存?

来源&#xff1a;https://www.cnblogs.com/semi-sub/p/13548479.html在使用spring框架的日常开发中&#xff0c;bean之间的循环依赖太频繁了&#xff0c;spring已经帮我们去解决循环依赖问题&#xff0c;对我们开发者来说是无感知的&#xff0c;下面具体分析一下spring是如何解…

20款华丽的几何形状字体【免费下载】

这里手机的字体使用几何形状设计。流畅简洁的线条&#xff0c;完美的圆形的角度建立一个完整性的设计感。使用几何形状生成出每一个优雅而现代的字母。这些字体可以用于标题和正文。由于他们的设计适合任何干净简约设计&#xff0c;因此很受欢迎。向下滚动并下载这些免费几何字…

MySQL 精选 60 道面试题(含答案)

金三银四到了&#xff0c;给大家整理一些数据库必知必会的面试题。基础相关1、关系型和非关系型数据库的区别&#xff1f;关系型数据库的优点容易理解&#xff0c;因为它采用了关系模型来组织数据。可以保持数据的一致性。数据更新的开销比较小。支持复杂查询&#xff08;带 wh…

Spring Boot 如何解决多个定时任务阻塞问题?

大家好&#xff0c;我是不才磊哥~最近长文撸多了&#xff0c;有点累&#xff0c;今天来点简单的。今天这篇文章介绍一下Spring Boot 中 如何开启多线程定时任务&#xff1f;为什么Spring Boot 定时任务是单线程的&#xff1f;想要解释为什么&#xff0c;一定要从源码入手&#…

mysql之explain

⊙ 使用EXPLAIN语法检查查询执行计划 ◎ 查看索引的使用情况 ◎ 查看行扫描情况⊙ 避免使用SELECT * ◎ 这会导致表的全扫描 ◎ 网络带宽会被浪费话说工欲善其事&#xff0c;必先利其器。今天就简单介绍下EXPLAIN。 内容导航 idselect_typetabletypepossible_keyskeyke…

SpringCloud OpenFeign + Nacos正确打开方式!

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;Nacos 支持两种 HTTP 服务请求&#xff0c;一个是 REST Template&#xff0c;另一个是 Feign Client。之前的文章咱们介绍过…

为什么创建线程池一定要用ThreadPoolExecutor?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 Java 语言中&#xff0c;并发编程都是依靠线程池完成的&#xff0c;而线程池的创建方式又有很多&#xff0c;但从…

Objects.equals有坑

前言最近review别人代码的时候&#xff0c;发现有个同事&#xff0c;在某个业务场景下&#xff0c;使用Objects.equals方法判断两个值相等时&#xff0c;返回了跟预期不一致的结果&#xff0c;引起了我的兴趣。原本以为判断结果会返回true的&#xff0c;但实际上返回了false。记…

Java 18 正式发布,默认 UTF-8,finalize 被弃用,别再乱用了!

JDK 18 正式发布JDK 17 刚发布半年&#xff0c;JDK 18 又如期而至&#xff0c;JDK 版本号这算是成年了&#xff1f;JDK 18 发布了&#xff0c;栈长继续为大家解读&#xff01;JDK 18 延续了 JDK 17 开创的免费策略&#xff0c;但&#xff0c;JDK 18&#xff5e;20 不是长期支持…

Spring官方推荐的@Transactional还能导致生产事故?

在Spring中进行事务管理非常简单&#xff0c;只需要在方法上加上注解Transactional&#xff0c;Spring就可以自动帮我们进行事务的开启、提交、回滚操作。甚至很多人心里已经将Spring事务与Transactional划上了等号&#xff0c;只要有数据库相关操作就直接给方法加上Transactio…

京东二面:MySQL 主从延迟、读写分离 7 种解决方案!

我们都知道互联网数据有个特性&#xff0c;大部分场景都是 读多写少&#xff0c;比如&#xff1a;微博、微信、淘宝电商&#xff0c;按照 二八原则&#xff0c;读流量占比甚至能达到 90%结合这个特性&#xff0c;我们对底层的数据库架构也会做相应调整。采用 读写分离处理过程&…

再见Postman,这款API神器更好用!

代码未动&#xff0c;文档先行其实大家都知道 API 文档先行的重要性&#xff0c;但是在实践过程中往往会遇到很多困难。程序员最讨厌的两件事&#xff1a;1. 写文档&#xff0c;2. 别人不写文档。大多数开发人员不愿意写 API 文档的原因是写文档短期收益远低于付出的成本&#…

如何保证数据库和缓存双写一致性?

前言数据库和缓存&#xff08;比如&#xff1a;redis&#xff09;双写数据一致性问题&#xff0c;是一个跟开发语言无关的公共问题。尤其在高并发的场景下&#xff0c;这个问题变得更加严重。我很负责的告诉大家&#xff0c;该问题无论在面试&#xff0c;还是工作中遇到的概率非…

面试官:AtomicInteger是如何保证线程安全?

blog.csdn.net/nanhuaibeian/article/details/120936139一、为什么引入 AtomicInteger &#xff1f;谈到线程安全&#xff0c;会首先想到了synchronized 和 Lock,但是这种方式又有一个名字&#xff0c;叫做互斥锁&#xff0c;一次只能有一个持有锁的线程进入&#xff0c;再加上…

机器学习 训练验证测试_测试前验证| 机器学习

机器学习 训练验证测试In my previous article, we have discussed about the need to train and test our model and we wrote a code to split the given data into training and test sets. 在上一篇文章中&#xff0c;我们讨论了训练和测试模型的必要性&#xff0c;并编写了…

如何判断线程池已经执行完所有任务了?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;很多场景下&#xff0c;我们需要等待线程池的所有任务都执行完&#xff0c;然后再进行下一步操作。对于线程 Thread …

IRCTC的完整形式是什么?

IRCTC&#xff1a;印度铁路餐饮和旅游公司 (IRCTC: Indian Railways Catering and Tourism Corporation) IRCTC is an abbreviation of Indian Railways Catering and Tourism Corporation. It is a subsidiary of the Indian Railway established by the Ministry of Railways…

分布式锁的 3 种实现方案!

前言 大家好&#xff0c;我是磊哥。今天跟大家探讨一下分布式锁的设计与实现。希望对大家有帮助&#xff0c;如果有不正确的地方&#xff0c;欢迎指出&#xff0c;一起学习&#xff0c;一起进步哈~分布式锁概述数据库分布式锁Redis分布式锁Zookeeper分布式锁三种分布式锁对比1.…