[ Java ]中的线程池工作原理,为什么不是先创建线程而是先往阻塞队列里塞任务?

149 天前
 lsk569937453

假设核心线程数为 n,最大线程数为 m 。线程池创建后,就提交了 n 个任务且这 n 个任务一直在执行,没有结束。此时再提交一个任务就会塞到任务队列里。我的疑问是新提交的这个任务为什么不是创建一个新的线程执行?。线程池不是应该首先要保证任务完成吗?

现在的逻辑是"先判断任务队列是否满再判断是否达到最大线程数",这样设计有什么优点呢?

6869 次点击
所在节点    Java
86 条回复
iosyyy
149 天前
@iosyyy 提个问搞的跟你是我 leader 一样这年头真是什么人都有..
另外单纯讨论问题 这个问题本质上是 java 实现线程池时的取舍

你能提出这个问题代表你本质上并没有理解什么是核心线程 什么是最大线程
最大线程是容错 而核心线程才是真正的执行者

你把最大线程理解为了执行者而不是容错
MoYi123
149 天前
“线程池不是应该首先要保证任务完成吗?”

不是, 线程池主要是为了省资源, 减少创建/销毁线程的次数.
你这样做明显会增加创建/销毁线程的次数.
kenvix
149 天前
你这不是显然的? CPU 密集型,超过 CPU 核数的线程没有任何意义。IO 密集型,用虚拟线程去。
whileFalse
149 天前
@salmon5 你的这个合理在哪儿?核心线程数的意义是什么?
salmon5
149 天前
@whileFalse 我想了下,还是直接创建新线程合理,核心线程都在用了,就别到队列等核心线程了,现在云计算扩容很快。核心线程不够用,就直接创建新线程。最终不够就横向+纵向扩容。
如果扔到队列,等核心线程,结果就是”服务慢、服务器空闲“。
burymme11
149 天前
新提交的这个任务为什么不是创建一个新的线程执行?线程池不是应该首先要保证任务完成吗?
当然不是了,线程池的核心一直就是维护好线程资源,在任务执行效率和资源消耗两者之间做优先级考虑,Doug Lee 选了后者,当时全世界的 Java 大牛们,也认可了他的选择。
PS:线程池的源码是 Doug Lee 写的,距今估计快 20 年了,那个时候的硬件条件和现在根本没法比,我也觉得这个设计目前来看有点小瑕疵,有点落后时代了。

"先判断是否达到最大线程数在判断任务队列是否满了"为什么不可以?
当然可以,你完全可以自己实现一个。
LiaoMatt
149 天前
@salmon5 Tomcat 很多其实是 IO 密集型请求, 为了降低响应时间, 这么做可以理解, 因为客户端还等着呢, 每个场景有自己测重点; JUC 牺牲一点实时性, 减少系统资源消耗, 在异步执行实时性要求不高的任务, 这个设计挺好的
dode
149 天前
你可以配置线程池的最大工作线程为设备线程数量 m ,

有的线程池场景是业务资源不够,限制处理速度
有的线程池是存计算可以把设备 CPU 使用达到 100%
q727729853
149 天前
菜鸟的个人理解,不对勿喷。
tomcat 是用来处理请求的,我们不应该让此成为我们系统的瓶颈,因此应该尽最大努力去执行。
java 中的线程池更强调的灵活配置,更方便各个场景的使用。(你可以通过设置队列类型、拒绝策略来实现)

至于附言中说的可不可以。那当然是可以。
最后,问:难道我们不能通过修改线程池的配置,来达到类似 tomcat 线程池的效果吗?(注意哦,是类似)
whileFalse
149 天前
@salmon5 大哥你先动动脑子,如果你直接略过“核心线程”配置不断创建新线程,是不是说明你的配置数值设置的不合理呢?
TtTtTtT
149 天前
个人的猜测,是因为根据线程池目前的设计,当线程池扩张到最大线程数 m 后,从 m 减小到 n 是很困难的。因此,在扩张线程数上,采取了更为谨慎的策略。

根据实践,当线程数扩张到最大线程数后,少量的任务就可以满足 Keep Alive Time 。只有在线程池完全不再有新的任务时,才能回缩到核心线程数。
kneo
149 天前
你问的没错,这个策略是有点奇葩。

core size 的设置更像是为 CPU 类型的计算服务的,所以满了就不创建。但是又设置了个 max size ,然后又只有在队列满了的时候才工作……

因为这个队列就是给你存等待任务的,队列满了它看实在不行了,存不下了,才会不情愿的给你分配线程……

所以,根据你的需求,你用 fixed thread pool 就好了(其实就是 core size 和 max size 调成一样的了)。
justplaymore
149 天前
java 线程池设计是分为任务管理和线程管理,典型的生产者消费者模式,由任务队列生产消息,由线程池消费消息。

“新提交的这个任务为什么不是创建一个新的线程执行?”

当发生大量并发提交任务时,使用队列可以做到无锁化,而如果直接使用线程池需要进行加锁。
由任务队列统一接管任务提交,由线程池控制从任务队列中取任务,将任务队列和线程池解耦,他们之间只通过消息进行交互。
admol
149 天前
问的问题有点奇怪。

这个队列不就是"池"吗?

既然是池,是不是要判断池子满了没满?

"先判断任务队列是否满再判断是否达到最大线程数"

这里的满,不是指一个池子最上面边缘的刻度,可能是边缘下面一点。就比如水瓶满 500ml ,但是实际可能水并没有装到瓶口口沿,这样设计估计也是为了留出一定的膨胀空间(代码就是留出一定的更大并发能力)。

满就是线程池设计的标准并发能力。
最大线程数是线程池的最大并发能力。
cybort
149 天前
因为队列没满说明当前的线程处理能力能 hold 住你丢任务的速度。
ma836323493
149 天前
先判断是否达到最大线程数在判断任务队列是否满了"为什么不可以?

你想这样做, 为什么不把核心线程设置大一点呢?
Plutooo
149 天前
Java 历史包袱太重
wetalk
149 天前
OP 需要的是这个 org.apache.tomcat.util.threads.ThreadPoolExecutor ,和 java.util.concurrent.ThreadPoolExecutor 最大区别在于,优先打满 maximumPoolSize ,至于为何如此设计,就藏在类的 doc 文档中
7911364440
149 天前
创建线程成本比较高,有可能创建新线程的过程中已经有任务执行完成了,这样新创建的线程就是多余的了
gongxuanzhang
149 天前
根据业务设置不同的参数就可以达到目的,你把线程池看成一个 MQ,第一要务是异步,而不是快速解决. 没有人能保证你任何任务快速解决

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1036271

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX