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

b680f69e3cb5b110c5e2d03d07d70c6d.png

作者 | 磊哥

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

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

在 Java 语言中,并发编程都是依靠线程池完成的,而线程池的创建方式又有很多,但从大的分类来说,线程池的创建总共分为两大类:手动方式使用 ThreadPoolExecutor 创建线程池和使用 Executors 执行器自动创建线程池。那究竟要使用哪种方式来创建线程池呢?我们今天就来详细的聊一聊。

先说结论

在 Java 语言中,一定要使用 ThreadPoolExecutor 手动的方式来创建线程池,因为这种方式可以通过参数来控制最大任务数和拒绝策略,让线程池的执行更加透明和可控,并且可以规避资源耗尽的风险。

OOM风险演示

假如我们使用了 Executors 执行器自动创建线程池的方式来创建线程池,那么就会存现线程溢出的风险,以 CachedThreadPool 为例我们来演示一下:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExecutorExample {static class OOMClass {// 创建 1MB 大小的变量(1M = 1024KB = 1024*1024Byte)private byte[] data_byte = new byte[1 * 1024 * 1024];}public static void main(String[] args) throws InterruptedException {// 使用执行器自动创建线程池ExecutorService threadPool = Executors.newCachedThreadPool();List<Object> list = new ArrayList<>();// 添加任务for (int i = 0; i < 10; i++) {int finalI = i;threadPool.execute(new Runnable() {@Overridepublic void run() {// 定时添加try {Thread.sleep(finalI * 200);} catch (InterruptedException e) {e.printStackTrace();}// 将 1M 对象添加到集合OOMClass oomClass = new OOMClass();list.add(oomClass);System.out.println("执行任务:" + finalI);}});}}
}

第 2 步将 Idea 中 JVM 最大运行内存设置为 10M(设置此值主要是为了方便演示),如下图所示:ee2de3c3e9dcfde3c694fbbe24e85271.png以上程序的执行结果如下图所示:1897487c943a75488eb06175931926d4.png从上述结果可以看出,当线程执行了 7 次之后就开始出现 OutOfMemoryError 内存溢出的异常了。

内存溢出原因分析

想要了解内存溢出的原因,我们需要查看 CachedThreadPool 实现的细节,它的源码如下图所示:f245f8f48dcf3b526f89b3fde8638dc0.png构造函数的第 2 个参数被设置成了 Integer.MAX_VALUE,这个参数的含义是最大线程数,所以由于 CachedThreadPool 并不限制线程的数量,当任务数量特别多的时候,就会创建非常多的线程。而上面的 OOM 示例,每个线程至少要消耗 1M 大小的内存,加上 JDK 系统类的加载也要占用一部分的内存,所以当总的运行内存大于 10M 的时候,就出现内存溢出的问题了。

使用ThreadPoolExecutor来改进

接下来我们使用 ThreadPoolExecutor 来改进一下 OOM 的问题,我们使用 ThreadPoolExecutor 手动创建线程池的方式,创建一个最大线程数为 2,最多可存储 2 个任务的线程池,并且设置线程池的拒绝策略为忽略新任务,这样就能保证线程池的运行内存大小不会超过 10M 了,实现代码如下:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;/*** ThreadPoolExecutor 演示示例*/
public class ThreadPoolExecutorExample {static class OOMClass {// 创建 1MB 大小的变量(1M = 1024KB = 1024*1024Byte)private byte[] data_byte = new byte[1 * 1024 * 1024];}public static void main(String[] args) throws InterruptedException {// 手动创建线程池,最大线程数 2,最多存储 2 个任务,其他任务会被忽略ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 2,0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2),new ThreadPoolExecutor.DiscardPolicy()); // 拒绝策略:忽略任务List<Object> list = new ArrayList<>();// 添加任务for (int i = 0; i < 10; i++) {int finalI = i;threadPool.execute(new Runnable() {@Overridepublic void run() {// 定时添加try {Thread.sleep(finalI * 200);} catch (InterruptedException e) {e.printStackTrace();}// 将 1m 对象添加到集合OOMClass oomClass = new OOMClass();list.add(oomClass);System.out.println("执行任务:" + finalI);}});}// 关闭线程池threadPool.shutdown();// 检测线程池的任务执行完while (!threadPool.awaitTermination(3, TimeUnit.SECONDS)) {System.out.println("线程池中还有任务在处理");}}
}

以上程序的执行结果如下图所示:0bd357fbff28a255f0ea7d997fc1e178.png从上述结果可以看出,线程池从开始执行到执行结束都没有出现 OOM 的异常,这就是手动创建线程池的优势。

其他创建线程池的问题

除了 CachedThreadPool 线程池之外,其他使用 Executors 自动创建线程池的方式,也存在着其他一些问题,比如 FixedThreadPool 它的实现源码如下:8ab79c0d46cdd86b6b113b8fe34009a0.png而默认情况下任务队列 LinkedBlockingQueue 的存储容量是 Integer.MAX_VALUE,也是趋向于无限大,如下图所示:bb32734db91fd504b25ebf4e5cca03df.png这样就也会造成,因为线程池的任务过多而导致的内存溢出问题。其他几个使用 Executors 自动创建线程池的方式也存在此问题,这里就不一一演示了。

总结

线程池的创建方式总共分为两大类:手动使用 ThreadPoolExecutor 创建线程池和自动使用 Executors 执行器创建线程池的方式。其中使用 Executors 自动创建线程的方式,因为线程个数或者任务个数不可控,可能会导致内存溢出的风险,所以在创建线程池时,建议使用 ThreadPoolExecutor 的方式来创建

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

公众号:Java面试真题解析

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

94eba46c4e2bd0afb0b671f12bd749fa.gif

往期推荐

a4e114265e5d8c8b5c7e828987ab6371.png

面试突击31:什么是守护线程?它和用户线程有什么区别?


769dc5bcf824f9ec336fc82cb456979c.png

面试突击30:线程池是如何执行的?拒绝策略有哪些?


4ae5b6779e3d4246515f33d305e0bb93.png

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


4a46d2999390bce19f1bd20f93b983cc.gif

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

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

相关文章

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;通过它我…

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

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

array_keys_PHP array_keys()函数与示例

array_keysPHP array_keys()函数 (PHP array_keys() function) array_keys() function is used to get the keys of an array, it accepts an array as an argument and returns a new array containing keys. array_keys()函数用于获取数组的键&#xff0c;它接受一个数组作为…

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

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

strtolower_PHP strtolower()函数与示例

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

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

前言数据库和缓存&#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.…

java学习笔记16--异常

java学习笔记16--异常 异常 异常时导致程序中断运行的一种指令流&#xff0c;如果不对异常进行正确的处理&#xff0c;则可能导致程序的中断执行&#xff0c;造成不必要的损失&#xff0c; 所以在程序的设计中必须要考虑各种异常的发生&#xff0c;并正确的做好相应的处理&am…

ruby hash添加数据_如何在Ruby中向Hash添加元素?

ruby hash添加数据Before going through the ways to add elements to the hash instances, let us understand what could be called as a hash element. So, Hash is the collection of keys and their values. For example, 在介绍向哈希实例添加元素的方法之前&#xff0c;…

线程安全问题的 3 种解决方案!

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;线程安全是指某个方法或某段代码&#xff0c;在多线程中能够正确的执行&#xff0c;不会出现数据不一致或数据污染的…

黑色30s高并发IIS设置

在这篇博文中&#xff0c;我们抛开对阿里云的怀疑&#xff0c;完全从ASP.NET的角度进行分析&#xff0c;看能不能找到针对问题现象的更合理的解释。 “黑色30秒”问题现象的主要特征是&#xff1a;排队的请求&#xff08;Requests Queued&#xff09;突增&#xff0c;到达HTTP.…