Java多线程之线程池ThreadPoolExecutor

嘿嘿,你的分析挺详细的,不过感觉就像在读论文一样。
我们换个方式来谈谈这个ThreadPoolExecutor?
上周一位客户问我有关 Java 多线程的问题。
他正好踩到了ThreadPoolExecutor的一个陷阱。
让我告诉你一些引起我注意的事情,看看我是否正确。

---
1 、配置参数一开始很头疼
你写的配置,ThreadPoolExecutor(5 ,1 0,2 00,TimeUnit.MICROSECONDS,new ArrayBlockingQueue(5 ));很头疼。


核心线程数为5 ,最大线程数为1 0,属于正常现象。

但是那个2 00微秒的keepAliveTime...我去年在杭州做一个项目,把它设置为2 00微秒。
结果线程一创建就几秒就被销毁了,CPU 飙升。
然后改成1 0秒才稳定。
因此,这些参数必须根据实际任务时间确定,不能随意设置。

队列是ArrayBlockingQueue(5 ),这实际上是一个陷阱。
当队列已满时会发生什么?超出任务直接抛出异常!你的第 1 5 个任务没有执行,因为没有地方可以放置它。
我见过有人因此在网上下跪。

---
2 .作业过程就像一场战争
你划分的阶段相当清晰:

前5 个作业直接进入核心序列,没有问题。

第6 -1 0个任务被放入队列中,这是正常的。

但是1 1 月初发生了一些事情。
队伍中只有 5 个位置。
如果传了1 5 ,接下来的4 个任务就会直接报RejectedExecutionException。
默认的拒绝政策是残酷的!
你对Thread.sleep(4 000)的模拟花费了相当长的时间,可以看出线程执行顺序很乱。
核心线程先完成自己的处理,然后为非核心线程腾出空间。
在这种情况下,task1 0 可能会先于 task4 运行,这是非常神奇的。

---
3 .主要现象描述正确

线程数超过corePoolSize:当队列满时,创建非核心线程,最多达到maxPoolSize(在你的例子中,它是1 0)。
这是正确的。

队列大小限制:ArrayBlockingQueue 是完全严格的。
用完就满了,溢出就挂了。
上次改项目,我改成了LinkedBlockingQueue。

任务完成顺序不规则:这确实是事实。
非核心线程都是临时工作者。
它们按照自己的节奏运行,与核心线程竞争 CPU。

---
4 、可能出现的问题及优化建议
您提到的优化建议比较相关:

keepAliveTime:2 00微秒太长,改成一秒或者更长。

队列选择:确实,如果任务数量很大,ArrayBlockingQueue就会望而却步。
但是LinkedBlockingQueue很容易失控(无限长的队列)。
根据经验,我通常使用 LinkedBlockingQueue(1 00)。

拒绝策略:这绝对是优先事项!默认抛出异常太粗暴了。
上次网上有bug,因为没有处理拒绝策略,直接转储日志。
我建议调整策略,例如记录日志然后放弃,或者开发降级逻辑。

---
5 .代码改进示例
你的改进示例非常好:
java ThreadPoolExecutor 执行器 = new ThreadPoolExecutor( 5 , 1 0, 6 0, 时间单位.秒, 新的 LinkedBlockingQueue(1 0)。
ThreadPoolExecutor.CallerRunsPolicy()新的 );

keepAliveTime 改为 6 0 秒,并没有那么极端。

队列容量增加到1 0个,缓冲能力更强。

CallerRunsPolicy 有趣,将任务转储到提交线程并自行运行?这样可以避免线程过多,但提交任务的线程可能会变慢。
是否这样做取决于业务场景。

---
摘要
您总结得对。
ThreadPoolExecutor 是一把多线程瑞士军刀。
不管参数调整得好不好,性能可能会差好几倍。
关键是:
1 了解您的任务是计算密集型还是 IO 密集型,这决定了如何设置 corePoolSize 和 maxPoolSize。
2 . 队列大小应根据任务交付频率和执行时间来确定。
不要死板。
3 、拒绝策略一定要解决,否则你会在网上遇到阎王。

但归根结底,最好的办法还是使用Executors工具类自带的newFixedThreadPool(1 0)或者newCachedThreadPool()。
它简单且无麻烦,尽管性能不如定制它。
但我们不需要每天构建高性能线程池,不是吗?
无论如何,你必须考虑一下。
这个问题没有标准答案。

Java多线程之ThreadPoolExecutor原理(图文代码实例详解)

哈啰普惠Java面经

你好,普惠Java面试。
去年我帮一个朋友做了一次采访,真的很感动。
对于他们来说,主要看你的基础是否扎实,做过的项目好不好,能不能解决问题。

以线程池为例。
那年朋友去的时候,被问到核心线程数和最大线程数这个参数是什么意思。
他表示,根据他平时的做法,CPU密集型任务的核心线程数与CPU核心数相同,IO密集型任务的核心线程数则更高。
至于排队情况,他表示这取决于业务需求。
如果您希望速度更快,请使用直队列。
如果要排队,请使用无限队列。
无论如何,不​​要被卡住。
至于拒绝该政策,他表示将根据公司的要求而定。
如果他能忍受,他就使用CallerRunsPolicy,如果他不能忍受,他就使用AbortPolicy。

就ES而言,我朋友说他去年也考过。
他表示,ES分为多个节点,可以快速探索和扩展。
他将倒排索引比作在字典中查找单词,它会告诉您哪些页面包含该单词。
他说,评分取决于你搜索的术语在文档中的重要性、它出现在多少文档中以及综合计算。

JVM,去年我也考了很多。
朋友说垃圾回收算法是mark-clear、mark-organize、copy,他解释得很清楚。
G1 和CMS,他说G1 具有可预测的暂停,适合更大的内存堆; CMS并发运行,延迟短,但内存容易损坏。
我从来没有见过这样的事情,所以我不敢乱说。

去年还要求算法题。
朋友说到倒水的问题,他就一步步解决,终于成功了。
他说,要对大文件进行排序,请按块读取它们,将它们组织成更小的块,最后将它们组合起来。
对于两个数字的和,他使用哈希表,效率很高。

就第二次面试来说,主要看你的项目。
我的朋友选择了一个他做过的项目,从头到尾讲了一遍,包括他遇到的问题以及他是如何解决的。
Spring集成MyBatis时,要求按照从网上复制的步骤进行,这是正确的。
在开发初学者时,他们只讨论了核心元素、自动配置、条件注释和 Spring.Factories。
他认识他们所有人。

系统设计去年也进行了测试。
我的朋友说服务A发送文件到服务B,他说文件是分部分上传的。
必须要做,断点处可以恢复传输,数据也需要验证。
这个挺实用的,朋友说面试官还蛮满意的。

总的来说,面试需要充分的准备,尤其是必须掌握的核心技术和算法问题。
项目经验方面,多想想自己做过的项目,是怎么做的,主要特点是什么,缺点是什么。
在系统设计的时候,要多阅读资料,学习别人的想法。

八篇文章虽然有一些方面,但基础还是需要打牢的。
我的朋友说他在面试前回顾了Spring和MyBatis。
因此,他在采访中问了非常详细的问题。
他很高兴自己已经做好了充分的准备。