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

2024-04-28 09:48:06 +08:00
lsk569937453  lsk569937453

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

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

8655 次点击
所在节点   Java  Java
86 条回复
iosyyy
iosyyy
2024-04-28 11:16:39 +08:00
@iosyyy 提个问搞的跟你是我 leader 一样这年头真是什么人都有..
另外单纯讨论问题 这个问题本质上是 java 实现线程池时的取舍

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

你把最大线程理解为了执行者而不是容错
MoYi123
MoYi123
2024-04-28 11:16:53 +08:00
“线程池不是应该首先要保证任务完成吗?”

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

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

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

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

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

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

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

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

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

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

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

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

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

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

满就是线程池设计的标准并发能力。
最大线程数是线程池的最大并发能力。
cybort
2024-04-28 12:06:46 +08:00
因为队列没满说明当前的线程处理能力能 hold 住你丢任务的速度。
ma836323493
2024-04-28 13:40:11 +08:00
先判断是否达到最大线程数在判断任务队列是否满了"为什么不可以?

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

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

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

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

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

© 2021 V2EX