工作后,接触过定义线程池的情况只有全局一个线程池,由前辈设置,自己用就可以了。再就是使用 Springboot 提供的 @Async ,想知道大家一般在生产中的线程池是怎么定义的和使用的
感觉多线程这一块的技术还是非常多内容,无论是思想还是实细节,我常常在想 node 真的好优雅啊,提供简单的 api 就很方便实现异步执行,其他语言里面好像要知道很多细节才可以编写多线程代码
由于本人技术菜单一批,可能有的问题比较蠢,恳请大家不吝赐教
1
zznote 312 天前
不想打字,你可以选择直接问 gpt ,它比我们大部分人说的明白
|
2
baolinliu442k OP 以下是来自 chatgpt 的回答
1. 线程池定义在哪里? 线程池的定义通常应该放在一个全局的工具类(如 ThreadPoolUtil )或者专门的配置类中。这样做有助于集中管理和配置线程池,提高代码的可维护性。在业务类中定义线程池(例如在 xxxService 的 private static )通常不是最佳实践,因为线程池的配置和管理应该是全局的,而不仅仅局限于某个业务类。 2. 单个线程池执行所有任务还是多个线程池? 是否使用单个线程池还是多个线程池取决于任务的特性和要求。一般来说,使用单个线程池可能会更简单,但在某些情况下,使用多个线程池可能更合适。多个线程池可以根据任务的类型、优先级、执行时间等因素进行分类和分配,以更好地满足不同任务的需求。 3. 多个线程池的优势和性能提升? 定义多个线程池的主要目的不仅仅是为了避免线程相互依赖,还可以根据任务的性质进行更细粒度的控制。这样可以提高资源的利用率,更好地适应不同类型的任务。性能提升的效果取决于具体的应用场景和任务类型,可能并不总是显著的。在某些情况下,使用多个线程池可能更适合任务的分配和调度。 4. 执行任务的方式? 在使用线程池执行任务时,可以使用 submit 方法或 execute 方法,具体选择取决于你对任务执行结果的需求。 |
3
w741069229 312 天前 via iPhone 1
谨慎用在生产
|
4
Leviathann 312 天前
异步和多线程本来就是两回事
以前搞一堆线程,大部分都是因为阻塞 io |
5
siweipancc 311 天前 via iPhone
node 并发叫优雅?你那叫异步回调,最常用的 debounce 底层还是必须有个有个异步池支持,玩过 rxjs 没?别说 Promise 一把梭╮( ̄▽ ̄"")╭
|
6
ffw5b7 311 天前 via Android
|
7
chendy 311 天前
按需,慎用,不到万不得已不给系统加这方面的复杂度
|
8
blankmiss 311 天前
@baolinliu442k V2EX 不允许在回复出现 gpt 回复内容
|
9
joyhub2140 311 天前
业务开发就别纠结了,改用什么就用什么。
线程池用的最多的是后端框架和客户端请求池,你没看错客户端也用的很多,而且频繁程度很高,http 异步也需要一个线程池管理着。 |
10
qhkobold 311 天前
用啥线程池呢,直接上 jdk21 用虚拟线程
|
11
mmdsun 311 天前
线程池 Spring 配置 bean 用的时候注入 + CompletableFuture
|
12
Seulgi 311 天前 1
全局,不管 util 类还是 spring bean 管理,都是其中一种实现方式。按需,小组自己评估哪些任务可以丢到哪些池里去,相当于给池定义他的领域,属于他领域的就用他,谁用了池,哪里用了池,要评估,不要一个人随便写个池,别人也随便在随便用你的池。慎用,除非没有其他办法,一般不用池。现在这个时间,一般公司用的都是 future 。Java21 的虚拟线程,现在可以忽略。生产项目都还没几个 Java21 实践。
|
13
kuituosi 311 天前 1
1.定义在合适的地方,要看具体的需求
2.同一类的任务放一个线程池,防止互相干扰,也便于优化 3.减少依赖和干扰,优化执行效率肯定提升性能 4.肯定看情况使用,CompletableFuture 最强大,不仅获取异步结果,而且可以设置超时和组合 java 的多线程已经提供足够好的库已经足够好用了,能够跟 java 多线程比的也就是协程了 node 这种半残语言只有一个线程,根本没有多线程。只适合 io 型工作,遇到 cpu 型就是废物。 不要提可以多进程 node 也能做 cpu 型工作,不仅麻烦不好维护,也难于优化 而 java 的多线程有优秀的框架,使用也比较简单,工程师更多时候花在优化参数上,而且可以胜任复杂的业务场景 node 跟 java 比完全没有一战之力 |
14
CodeCodeStudy 311 天前
底层类库用的,业务中谨慎使用
|
15
oneronan 311 天前
1. @Async 有坑,自定义线程池做异步任务。
2. 统一定义线程池工具类,统一构造器,统一优雅关闭,业务需要线程池使用构造器创建,具体的线程数量根据 io 、cpu 实际情况设定。 3. spring 的 scheduled 有坑,默认线程池核心数是 1 ,如果有很多任务,任务同间隔时间执行,会出现任务不执行的问题。解决问题:1. org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration#taskSchedulerBuilder 构建任务调度线程池,org.springframework.boot.autoconfigure.task.TaskSchedulingProperties 查看这个类,根据 @ConfigurationProperties("spring.task.scheduling")在配置文件中修改线程池相关初始化参数。2. 自定义任务调度线程池,使用 org.springframework.scheduling.config.ScheduledTaskRegistrar#setScheduler 注册。 |
16
Aresxue 311 天前
1.视业务而定,如果是一个低频的业务和其它业务共享一个线程池也无伤大雅,如果相对并发较高,最好是指定用自己的线程池而不是公用的线程池,@Async 也是可以指定线程池的,和 private static 的方式基本上是等价的,大多数情况下都可以用它;
2.参数没有标准,完完全全根据业务的情况而定,这个情况不仅是当下还有对未来的适当评估; 3.线程池主要是用于隔离线程资源和多参数任务并行降低 rt ,其对于整个应用资源的利用率并不会有显著的提升; 4. execute 适用于没有返回值的任务,submit 的返回值是 Future ,基本上能用 submit 没啥必要了,CompleteableFuture 本来就是为了加强 Future 的。 对于业务中我是建议能不能则不用,作为排名靠后的一个选择,顺便贴一些使用线程池的注意点: - 合适的任务队列及其大小,过大会造成 oom ; - ThreadLocal(登录信息上下文或其它的业务信息)丢失; - 全链路 id 丢失; - 合适的线程池策略和线程数(固定数目和不定数目); - 任务重启丢失(优雅退出); |
17
nothingistrue 311 天前
正确的讲,负责异步的是执行器,不是线程池。线程池只是执行器的组件,在此之外的组件还有任务队列、以及执行器的总控制。另外,不是所有执行器都需要线程池,你要高兴,完全可以用单线程搞个执行器。
执行器如何配置参数,直接看各 Excutor 类的 Javadoc 即可,压根不需要求别人。执行器在何时初始化、何时销毁、以及如何获取,这是个问题,但这个问题其实不是执行其的问题,而是如何将执行器放到 JVM 的问题。这个东西 Java 用熟了自然就回了,最简单的就是直接挂 static + 使用 static 代码块。 异步执行的原理就是这么复杂,你所谓的优雅,不是优雅,只是隐藏了底层 API ,同时也失去了定制能力的傻瓜式 API 而已。 |
18
imokkkk 311 天前
1.Bean 的方式创建、管理线程池
2.不同的业务使用不同的线程池 3.CompletableFuture 用起来很方便 目前我是这样用 没出过啥问题 @Configuration public class ThreadPoolConfig { @Bean(destroyMethod = "shutdown", name = "xxxxxThreadPool") public ThreadPoolExecutor xxxxxThreadPool() { ThreadFactory tf = new ThreadFactoryBuilder().setNameFormat("xxxxxThreadPool-%d").build(); return new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors() , Runtime.getRuntime().availableProcessors() * 2, xxx, TimeUnit.SECONDS, new ArrayBlockingQueue<>(xxxx), tf, new ThreadPoolExecutor.CallerRunsPolicy()); } @Bean(destroyMethod = "shutdown", name = "xxxxxThreadPool") public ThreadPoolExecutor xxxxxThreadPool() {} } |
19
northernsongy2 311 天前
注意区分业务场景,举个例子,你就明白了,有 2 个服务,A 调用 B ,B 服务有几次 mysql 超时,A 调用 B 的线程池,因为 B 的超时,全部等待,然后 A---挂了。(事后 B 还嘲讽了 A 的负责人,然后群里吵起来了....) 这个应该算生产比较容易出故障的场景
|
20
matepi 311 天前
@northernsongy2 没有 SLA 就是这样的啦。其实不给 SLA ,A 也有个办法就是坏一个丢一个(池扩张一),反正 B 的问题。
|
21
GloryJie 311 天前
在 BFF 层聚合数据的时候用的多,一般会同时调好几个接口。这边使用上线程池一般都托管给 Spring 。
最近在改造成基于 Dag 来编排任务执行了,不过基础还是让线程池执行 |
22
TuringHero 311 天前
spring:
threads: virtual: enabled: true |
24
baolinliu442k OP @siweipancc 没有,觉得 await 和 async 关键字挺好的
|
25
baolinliu442k OP @ffw5b7 这篇之前也看过
|
26
baolinliu442k OP @qhkobold 老项目 java8,我自己项目的话,我就直接 new Thread().start 了 哈哈
|
27
baolinliu442k OP @chendy 嗯嗯,公司的项目我还不敢随便配
|
28
baolinliu442k OP @blankmiss 哈哈不知道
|
29
baolinliu442k OP @joyhub2140 就是不知道用啥感觉
|
30
baolinliu442k OP @kuituosi 感谢回答,就是感觉 Node 单线程可以很方便进行异步挺好的,而且我也不知道啥算 CPU 密集型
|
31
baolinliu442k OP @CodeCodeStudy 嗯嗯,可是项目中定义线程池还是蛮普遍的
|
32
baolinliu442k OP @oneronan 感谢回答
|
33
baolinliu442k OP @Aresxue 谢谢回答, 我还有个疑问如果一个项目中定义了多个线程池,例如 2 个线程池,核心线程数都是 5 ,机器 cpu 核数是 5 , 那么可以同时执行 10 个任务吗? 线程池定义多了是不是作用不大了
|
34
baolinliu442k OP @nothingistrue 大佬,我茅厕顿开
|
35
baolinliu442k OP @Seulgi 确实需要慎用, 目前公司做的 toB 业务,一个大接口响应 10s 都不要优化
|
36
baolinliu442k OP @imokkkk 谢谢回答,感谢贴出代码, 很有参考意义
|
37
baolinliu442k OP @TuringHero 感觉 java 已经很先进了, 然而项目还是 java8hah
|
38
baolinliu442k OP @hdfg159 这么爽的嘛
|
39
ymy3232 311 天前
我们线上业务不复杂但是并发高而且时长要求严格,项目全局用一个公共的线程池,包括 springweb 和 CompleteableFuture 等一些组件的默认线程池都替换了,好处坏处都有,视项目而定
|
40
Plutooo 311 天前
可以看一下 rocketmq 源码里面是如何使用线程池的,全局搜索一下就可以
|
41
Aresxue 310 天前
@baolinliu442k 并发度和 cpu 核数有关系但没太大关系,cpu 哪怕是单核因为时间片是轮转的从使用者视角来看任务都是并行的,回到你这个问题从普通的使用者视角可以认为同时执行 10 个任务,但如果是按真实的占用 cpu 去执行逻辑这个角度,哪怕你有 5 个核,这 5 个核同时被你的任务使用的时间几乎是没有的,因为还有 tomcat 线程、rpc 线程池等其它活跃的线程共享你的 5 个核。
|
42
Aresxue 310 天前
@Aresxue 其实可以把虚拟线程学起来了,有了这玩意之后绝大多数场景就不再需要线程池了,线程不会再成为应用的瓶颈,目前 jdk21 中的功能已经勉强可用,预计下个 LTS 版本能基本稳定下来。
|
43
java123 309 天前
Vert.x 、Parseq
|