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

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 作为配置…

c语言宏函数怎么传递宏参数_C语言中的宏参数评估

c语言宏函数怎么传递宏参数We can define a function like Macro, in which we can pass the arguments. When a Macro is called, the Macro body expands or we can say Macro Call replaces with Macro Body. 我们可以定义一个函数&#xff0c;例如Macro&#xff0c;可以在其…

WebBrowser中html元素如何触发winform事件

这个问题来自论坛提问&#xff0c;对dom稍微了解的话还是比较简单的&#xff0c;只要注册一下事件就可以了。 C#代码如下: using System;using System.ComponentModel;using System.Windows.Forms;namespace WindowsApplication5...{  public partial class Form1 : Form  …

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

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

第四代编程语言_几代编程语言

第四代编程语言几代编程语言 (Generations of programming language) Programming languages have been developed over the year in a phased manner. Each phase of developed has made the programming language more user-friendly, easier to use and more powerful. Each…

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

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

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

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

Python中的简单图案打印程序

Pattern 1: 模式1&#xff1a; ** ** * ** * * ** * * * *Code: 码&#xff1a; for row in range (0,5):for column in range (0, row1):print ("*", end"")# ending rowprint(\r)Pattern 2: 模式2&#xff1a; Now if we want to print nu…

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。之前的文章咱们介绍过…

array.unshift_Ruby中带有示例的Array.unshift()方法

array.unshiftArray.unshift()方法 (Array.unshift() Method) In this article, we will study about Array.unshift() Method. You all must be thinking the method must be doing something which is related to unshifting of objects in the Array instance. It is not as…

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

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

ruby array_Ruby中带有示例的Array.fill()方法(3)

ruby arrayArray.fill()方法 (Array.fill() Method) In this article, we will study about Array.fill() method. You all must be thinking the method must be doing something related to populate the Array instance. Well, we will figure this out in the rest of our …

Objects.equals有坑

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

strtoupper 小写_PHP strtoupper()函数与示例

strtoupper 小写PHP strtoupper()函数 (PHP strtoupper() function) strtoupper() function is a string function it accepts the string and returns an uppercase string. strtoupper()函数是一个字符串函数&#xff0c;它接受字符串并返回大写字符串。 Syntax: 句法&#…

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…

python函数实例化_用Python实例化函数

python函数实例化In terms of Mathematics and Computer science, currying is the approach/technique by which we can break multiple-argument function to single argument function. 从数学和计算机科学的角度来看&#xff0c; 柯里化是一种方法/技术&#xff0c;通过它我…