Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
在 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 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(设置此值主要是为了方便演示),如下图所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/efdd083c363a9b9c41ee5363b857c309.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=683&id=ueb99cd9c&margin=[object Object]&name=image.png&originHeight=1365&originWidth=2150&originalType=binary&ratio=1&rotation=0&showTitle=false&size=198199&status=done&style=none&taskId=ueeb2e01e-0a7f-4b57-a4fb-c19fd8e47eb&title=&width=1075)
以上程序的执行结果如下图所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/241d582f9175a1fe751befb3e7be71fa.png#clientId=ud56411a7-ab24-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=573&id=ud7b6df8e&margin=[object Object]&name=image.png&originHeight=1145&originWidth=2924&originalType=binary&ratio=1&rotation=0&showTitle=false&size=294569&status=done&style=none&taskId=ub5f0a310-0317-4cbc-86e7-daec5dd030c&title=&width=1462)
从上述结果可以看出,当线程执行了 7 次之后就开始出现 OutOfMemoryError 内存溢出的异常了。

内存溢出原因分析

想要了解内存溢出的原因,我们需要查看 CachedThreadPool 实现的细节,它的源码如下图所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/f5d6b1a0f01cb3d8cc3c5affb0782626.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=199&id=u5c9c7355&margin=[object Object]&name=image.png&originHeight=397&originWidth=1806&originalType=binary&ratio=1&rotation=0&showTitle=false&size=83232&status=done&style=none&taskId=uc90443de-70e3-4873-a054-3380de696d6&title=&width=903)
构造函数的第 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 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("线程池中还有任务在处理");}}
}

以上程序的执行结果如下图所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/4883cd229e15f09ea3dcc25d23a4e33a.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=260&id=ude5706f1&margin=[object Object]&name=image.png&originHeight=520&originWidth=1996&originalType=binary&ratio=1&rotation=0&showTitle=false&size=70067&status=done&style=none&taskId=u49f0d4bc-b1f0-49a8-8b4e-706075c0c4f&title=&width=998)
从上述结果可以看出,线程池从开始执行到执行结束都没有出现 OOM 的异常,这就是手动创建线程池的优势。

其他创建线程池的问题

除了 CachedThreadPool 线程池之外,其他使用 Executors 自动创建线程池的方式,也存在着其他一些问题,比如 FixedThreadPool 它的实现源码如下:
![image.png](https://img-blog.csdnimg.cn/img_convert/741be157ff698dd9564d53a566197cae.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=164&id=u0796b9b1&margin=[object Object]&name=image.png&originHeight=327&originWidth=1830&originalType=binary&ratio=1&rotation=0&showTitle=false&size=70565&status=done&style=none&taskId=ubf50d492-479e-4d94-9fb4-b3cb470e0ad&title=&width=915)
而默认情况下任务队列 LinkedBlockingQueue 的存储容量是 Integer.MAX_VALUE,也是趋向于无限大,如下图所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/8ce7ad1e0242f0852bdb65dd6386e184.png#clientId=ua864364e-f98a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=236&id=u43e680d2&margin=[object Object]&name=image.png&originHeight=472&originWidth=1911&originalType=binary&ratio=1&rotation=0&showTitle=false&size=78162&status=done&style=none&taskId=u0d4d184f-3189-4085-860a-5b0f559d3d8&title=&width=955.5)
这样就也会造成,因为线程池的任务过多而导致的内存溢出问题。其他几个使用 Executors 自动创建线程池的方式也存在此问题,这里就不一一演示了。

总结

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

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

公众号:Java面试真题解析

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

面试突击32:为什么创建线程池一定要用ThreadPoolExecutor?相关推荐

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

    作者 | 磊哥 来源 | Java面试真题解析(ID:aimianshi666) 转载请联系授权(微信ID:GG_Stone) 在 Java 语言中,并发编程都是依靠线程池完成的,而线程池的创建方式又 ...

  2. 为什么阿里巴巴禁止使用 Executors 创建线程池,而是通过 ThreadPoolExecutor 方式?...

    >>号外:关注"Java精选"公众号,菜单栏->聚合->干货分享,回复关键词领取视频资料.开源项目. 1. 通过Executors创建线程池的弊端 在创建线 ...

  3. 为什么阿里不允许用Executors创建线程池,而是通过ThreadPoolExecutor的方式?

    1.通过Executors创建线程池的弊端 在创建线程池的时候,大部分人还是会选择使用Executors去创建. 下面是创建定长线程池(FixedThreadPool)的一个例子,严格来说,当使用如下 ...

  4. java线程池使用详解ThreadPoolExecutor使用示例

    一 使用线程池的好处 二 Executor 框架 2.1 简介 2.2 Executor 框架结构(主要由三大部分组成) 1) 任务(Runnable /Callable) 2) 任务的执行(Exec ...

  5. inputstreamreader未关闭会导致oom_【搞定面试官】你还在用Executors来创建线程池?会有什么问题呢?

    前言 上文我们介绍了JDK中的线程池框架Executor.我们知道,只要需要创建线程的情况下,即使是在单线程模式下,我们也要尽量使用Executor.即: ExecutorService fixedT ...

  6. 阿里面试官鬼得很,问我为什么他们阿里要禁用Executors创建线程池?

    作者:何甜甜在吗 来源:http://rrd.me/eUh6V 看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,通过 ...

  7. 面试官:说一下线程池内部工作原理?

    以下文章来源方志朋的博客,回复"666"获面试宝典 作者:清泉 cnblogs.com/qingquanzi/p/8146638.html 随着cpu核数越来越多,不可避免的利用多 ...

  8. 为什么阿里巴巴要禁用 Executors 创建线程池?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 转自:掘金,作者:何甜甜在吗 juejin.im/post/5dc ...

  9. 为什么阿里巴巴要禁用Executors创建线程池?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:何甜甜在吗 juejin.im/post/5dc41c165 ...

最新文章

  1. Sphinx武林秘籍(上)
  2. java线程中等待_Java:线程中的Thread.sleep():没有等待
  3. 【Shell脚本】颜色显示
  4. Linux 内核链表 【转】
  5. Matlab | Matlab从入门到放弃(1)——变量
  6. centos Crontab
  7. 计算机标准符合,计算机专利申请要符合哪些标准
  8. 高等数学下-赵立军-北京大学出版社-题解-练习10.1
  9. AcWing 1960. 闪烁(状态压缩+枚举)
  10. 【C++】双边滤波器(bilateral filter)
  11. cpp调用python_从python ctypes调用CPP函数
  12. 一些常用的ajax框架
  13. 解决矢量地图与卫星地图叠加有偏移的两种方法
  14. 【DevOps】软件开发生命周期
  15. 无线Wifi密码之暴力破解篇(WPA)
  16. 怎么用计算机ping组播地址,windows – 使用’目标主机无法访问’从同一台计算机ping“回复”(没有到其他计算机的路由)...
  17. 英特尔DRM内核驱动程序默认启用PSR2省电功能
  18. 【转】【GO】9.go:linkname
  19. 家用无线TP-LINK路由器使用一段时间后,频繁断网解决办法之一
  20. Matlab如何在文件中写入空格和换行

热门文章

  1. Oracle服务器连接
  2. find_cmd函数分析
  3. Silverlight - IIS 7.5 部署SilverLight4网站以及问题解决
  4. 功能测试代码python_如何使您的Python代码更具功能性
  5. 计算机主板各模块复位,电脑主板复位电路工作原理分析
  6. java 技能鉴定_JAVA试题-技能鉴定
  7. 边际概率条件概率_数据科学家解释的边际联合和条件概率
  8. leetcode1052. 爱生气的书店老板(滑动窗口)
  9. 调用lambda_如何使用Lambda调用上下文动态设置超时
  10. 数据图表可视化_数据可视化十大最有用的图表