V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
shangzhanyu
V2EX  ›  Java

Java 最佳线程数怎么得来?

  •  
  •   shangzhanyu · 2021-01-05 19:17:24 +08:00 · 6037 次点击
    这是一个创建于 1179 天前的主题,其中的信息可能已经有所发展或是发生改变。
    根据公式:服务器端最佳线程数量=((线程等待时间+线程 cpu 时间)/线程 cpu 时间) * cpu 数量,算出来的数量不过百,但是实际情况 service 的线程数又设置的 500 甚至更多,又看了 HotSpot 用的 1:1 内核线程,搞懵逼了。。。
    50 条回复    2021-01-07 16:35:44 +08:00
    xbh1794970183564
        1
    xbh1794970183564  
       2021-01-05 19:18:57 +08:00
    这里的线程主要是一直在运行的线程数吧,hotspot 后台一直进行垃圾收集,线程数太多肯定影响使用,service 里面 500 多应该大部分都是 WAITING 状态,真正在运行的没多少
    xbh1794970183564
        2
    xbh1794970183564  
       2021-01-05 19:20:38 +08:00
    @xbh1794970183564 mark,蹲个大佬回答
    shangzhanyu
        3
    shangzhanyu  
    OP
       2021-01-05 19:20:55 +08:00
    @xbh1794970183564 是的,运行中的不多,所以没搞懂有些服务设置 500 有啥用。。。
    shaoyijiong
        4
    shaoyijiong  
       2021-01-05 19:23:39 +08:00
    其实那些写 service 的人根本就没考虑过你说的最佳线程数 😂
    xbh1794970183564
        5
    xbh1794970183564  
       2021-01-05 19:23:50 +08:00
    @shangzhanyu 我们服务启动有 200 线程左右,很多都是框架相关的,自己的线程池一般是 IO 任务 CPU*2 线程,计算密集就是 CPU 核心数
    php8
        6
    php8  
       2021-01-05 19:56:02 +08:00 via Android
    使用 eventloop 的 nonblocking 应用,eventloop 线程数设置为 CPU 核心数的 2 倍,这是个推荐值,超过这个值不能提高吞吐量,小于这个值降低吞吐上限,性能够用设置为 1 或 2 也行。

    blocking IO 密集型应用,比如 springmvc 那套 web,线程是用来等 IO 的,假设每个请求 IO 等待 1 秒,线程数≈qps,500 线程就是 500 qps,某些场景等待时间 5 秒,500 线程就是 100qps 。

    现代 linux 和 jvm,维持几千个等待 IO 的线程开销很小,所以很多地方推荐 200 或者 500 。

    计算公式是对的,但也要看具体情况,还有其他因素要考虑。
    liuch
        7
    liuch  
       2021-01-05 20:22:37 +08:00   ❤️ 2
    书上、网上这些所谓的公式,都偏理想化,实际指导意义不大。生产环境,最好是根据 ThreadPoolExecutor 的统计数据,分析做调整,最好支持动态。特别是微服务里面各种复杂的调用相关的
    YouLMAO
        8
    YouLMAO  
       2021-01-05 20:26:43 +08:00 via Android
    我们一天处理大概 2 万亿 request,c++, 单容器 16c 开 16 工作线程池,开这么多想干嘛,都是 epoll 了,io 不阻塞工作线程的
    pkwenda
        9
    pkwenda  
       2021-01-05 20:58:40 +08:00
    主要还是看 io 密集性,如果不密集 & 内存允许的情况,个人项目我开的比较随意 ( jvm 单个线程默认 1M+ 的内存)
    neoblackcap
        10
    neoblackcap  
       2021-01-05 21:05:26 +08:00   ❤️ 2
    我就提一个吧,你们都是跑在物理机上面吗?要不然还是按 @liuch 的说,根据统计模型来调吧。
    书本上的东西很理想化的。虚拟机的 CPU 调度就能将所谓的最优碾烂。
    Rorysky
        11
    Rorysky  
       2021-01-05 23:47:18 +08:00
    @YouLMAO 啥业务这么巨量
    90d0n
        12
    90d0n  
       2021-01-06 09:15:41 +08:00
    How to Find the Optimal Database Connection Pool Size
    A formula which has held up pretty well across a lot of benchmarks for years is that for optimal throughput the number of active connections should be somewhere near ((core_count * 2) + effective_spindle_count). Core count should not include HT threads, even if hyperthreading is enabled. Effective spindle count is zero if the active data set is fully cached, and approaches the actual number of spindles as the cache hit rate falls. Benchmarks of WIP for version 9.2 suggest that this formula will need adjustment on that release. There hasn't been any analysis so far regarding how well the formula works with SSDs.

    However you choose a starting point for a connection pool size, you should probably try incremental adjustments with your production system to find the actual "sweet spot" for your hardware and workload.

    Remember that this "sweet spot" is for the number of connections that are actively doing work. Ignore mostly-idle connections used for system monitoring and control when working out an appropriate pool size. You should always make max_connections a bit bigger than the number of connections you enable in your connection pool. That way there are always a few slots available for direct connections for system maintenance and monitoring.

    https://wiki.postgresql.org/wiki/Number_Of_Database_Connections
    90d0n
        13
    90d0n  
       2021-01-06 09:17:03 +08:00
    postgre 数据库推荐的设置方式, 一直在生产上这么设置没出啥问题
    shangzhanyu
        14
    shangzhanyu  
    OP
       2021-01-06 09:27:06 +08:00
    @YouLMAO 同感,设置那么多不知道干啥,大部分都在等待状态
    shangzhanyu
        15
    shangzhanyu  
    OP
       2021-01-06 09:32:31 +08:00
    @neoblackcap 对哦,跑在虚拟机上确实和物理机不一样。。。
    shangzhanyu
        16
    shangzhanyu  
    OP
       2021-01-06 09:38:26 +08:00
    @liuch 就是想搞懂为什么设置这么大的线程数还没问题,按理说这么多线程上下文切换行得通么,机器配置 8c 8g
    BBCCBB
        17
    BBCCBB  
       2021-01-06 09:51:41 +08:00
    根据压测得到.
    wysnylc
        18
    wysnylc  
       2021-01-06 10:07:53 +08:00
    尽信书,不如无书
    hangszhang
        19
    hangszhang  
       2021-01-06 10:18:17 +08:00   ❤️ 1
    美团有一篇文章说这个的, 大意是公式与生产有偏差, 需要做压测然后支持动态调整参数
    php8
        20
    php8  
       2021-01-06 10:18:46 +08:00 via Android
    @shangzhanyu IO 密集型应用,开那么多线程的作用就是等待,一个请求阻塞一个线程,如果阻塞 1 秒钟,开 10 个线程每秒只能处理 10 个请求,开 100 个性能就提高到 100qps 了。就算开几万个这类线程,切换开销都能小到忽略不计。
    shangzhanyu
        21
    shangzhanyu  
    OP
       2021-01-06 10:22:12 +08:00
    @wysnylc 不信书才有此问
    shangzhanyu
        22
    shangzhanyu  
    OP
       2021-01-06 10:24:16 +08:00
    @php8 这个我去再研究下,好像是这么回事
    YouLMAO
        23
    YouLMAO  
       2021-01-06 10:36:18 +08:00
    @php8 大厂 io 都是 epoll, 是 cpu 中断通知工作线程, 而工作线程从不停止等 io
    shangzhanyu
        24
    shangzhanyu  
    OP
       2021-01-06 10:49:31 +08:00
    @YouLMAO epoll -> tomcat 分配 -> 工作线程执行 java 程序,现在就差 jvm 和操作系统线程之间的关系了
    Jooooooooo
        25
    Jooooooooo  
       2021-01-06 11:04:52 +08:00
    你看 io, 如果 io 很多基本都在等 io, 那线程数就大点

    如果都是内存本地操作, 线程数和核数差不多就行
    emSaVya
        26
    emSaVya  
       2021-01-06 11:37:01 +08:00
    你们可以试试 用 Vizier 这种调参工具 来确定线程数
    laminux29
        27
    laminux29  
       2021-01-06 12:01:37 +08:00
    现代软硬件太复杂了,教科书里那种简单的公式只能用来参考。建议通过实际压测 + 2 分法来寻找这个值。
    YouLMAO
        28
    YouLMAO  
       2021-01-06 13:07:26 +08:00
    java nio/nio2 老早就是 epoll 异步的了, 不管 tomcat 还是 netty, 很多人在说 io 密集就多开 io 线程, 这是啥年代的网民????
    有数据才会触发的呀, io 触发后业务逻辑是在业务线程池完成的, io 线程池很少就能十万并发
    tairan2006
        29
    tairan2006  
       2021-01-06 13:11:36 +08:00
    你要自己压测,或者换 golang 无脑开协程…
    php8
        30
    php8  
       2021-01-06 13:20:58 +08:00 via Android
    @YouLMAO 同步阻塞 IO 依然是最主流的 Java 并发模型,tomcat 和 springboot 的默认 worker 线程数是 200 。异步回调写写 framework 和 infra 还行,拿来写业务逻辑有时会比较麻烦,大厂不论 C++还是 JVM 都搞了协程。
    fakeshadow
        31
    fakeshadow  
       2021-01-06 13:25:07 +08:00
    常见的异步并不能处理所有 IO(如果你用 io_uring 或者 IOCP 之类的当我没说),所以阻塞线程池的 IO 作用仍然是很大的。具体设置多少可以根据你的实际情况,一般上限高的都是 spin on load 。
    assiadamo
        32
    assiadamo  
       2021-01-06 13:32:22 +08:00
    线程池设置线程数可以在运行时动态调的,根本不慌
    weizhen199
        33
    weizhen199  
       2021-01-06 13:33:58 +08:00
    很多大佬提过了,自己压测。

    这个影响的因素包括了 cpu 架构,别试图整通项式了。 但是一个大致的范围还是能参考的.
    elintwenty
        34
    elintwenty  
       2021-01-06 13:36:50 +08:00
    美团技术团队的博客: https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html
    这个问题文章给的结论就是
    —— “也曾尝试着去追求线程池参数设置的合理性,但面对业界方案具体落地的复杂性、可维护性以及真实运行环境的不确定性,我们在前两个方向上可谓“举步维艰”。最终,我们回到线程池参数动态化方向上探索,得出一个且可以解决业务问题的方案,”
    YouLMAO
        35
    YouLMAO  
       2021-01-06 13:37:29 +08:00
    我只看过 morgan stanley 和 facebook 都是异步的, 我是感觉大陆所谓架构师在 500nm 芯片年代追 5nm
    Goldilocks
        36
    Goldilocks  
       2021-01-06 13:47:02 +08:00
    @YouLMAO 那不是异步。真正的异步 driven 的 io 只有 Windows 才有,linux 上是半残废没人用。
    YouLMAO
        37
    YouLMAO  
       2021-01-06 13:53:18 +08:00
    @Goldilocks 举个例子, 我随手一搜, tomcat 8 默认是 nio 或 nio2, linux 下是不是异步? 或者等价问题 8 个 io 线程在 tomcat 8 够不够用, 还是说本帖架构师:最好核数*2 甚至 pool size=上百的线程池
    gdcbhtd
        38
    gdcbhtd  
       2021-01-06 14:30:03 +08:00
    IO 密集型任务:2*CPU 数,因为 IO 密集型任务,线程不是一直在运行,所以可以配置多一点;
    CPU 密集型任务:因为一直在使用 CPU,所以要保证线程数不能太多,可以 CPU 数+1 ;
    shangzhanyu
        39
    shangzhanyu  
    OP
       2021-01-06 15:12:03 +08:00
    感觉又要默默的去复习一下操作系统了。。。
    xcstream
        40
    xcstream  
       2021-01-06 15:19:58 +08:00
    只要大于 cpu 核心数 小于千 区别应该不是很大
    shangzhanyu
        41
    shangzhanyu  
    OP
       2021-01-06 15:20:26 +08:00
    @xcstream 怎么讲?
    xcstream
        42
    xcstream  
       2021-01-06 15:25:06 +08:00
    开个压 cpu 的跑分软件, 线程数大于 cpu 核就可以跑满了
    ZiLong
        43
    ZiLong  
       2021-01-06 16:33:43 +08:00
    @Goldilocks 最近弄了个 io_uring,netty 作者也在跟进,在一个 incubator 分支,听他讲,测试效果蛮好的
    chendy
        44
    chendy  
       2021-01-06 16:38:51 +08:00
    @YouLMAO 后面还要一堆线程等数据库和其他东西的返回,实际上还是一请求一线程,这个模型效率一般但是胜在好写
    php8
        45
    php8  
       2021-01-06 17:12:27 +08:00 via Android
    @YouLMAO tomcat 8 的 connector 是 nio,但是 parse 完 request 后还得丢给 worker thread pool 去做业务逻辑,这个 pool 默认 200 个线程,干的事多半会阻塞线程
    RudyS
        46
    RudyS  
       2021-01-06 17:28:53 +08:00
    1:1 是基本思路吧,然后再是业务类型,要是 CPU 密集,多开也没意义呀。
    gaifanking
        47
    gaifanking  
       2021-01-06 18:06:57 +08:00
    公式不对吧 不是应该 N=Cpu 个数*Cpu 目标占有率*(1+W/C)吗
    W:Wait time
    C:Compute time
    然后提速极限=1/P P 为并发时必须串行的部分百分比。

    基于公式推荐的个数:
    IO 密集型 2*CPU
    计算型 CPU+1
    这个推荐不好使,我一个 Demo IO 密集型任务 我测试 12 核单机起 1 万多个最快
    gitdoit
        48
    gitdoit  
       2021-01-07 08:50:54 +08:00
    @shaoyijiong 抓到了,上班划水,等下来我办公室
    shangzhanyu
        49
    shangzhanyu  
    OP
       2021-01-07 16:31:37 +08:00
    @anjxue demo 和实际不能比吧,毕竟有执行时间、gc 等等
    shangzhanyu
        50
    shangzhanyu  
    OP
       2021-01-07 16:35:44 +08:00
    @RudyS hotspot 是 1:1 内核线程,linux 系统理论上限是好几万,但是实际弄这么多也不起作用好多都是休眠状态
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2868 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 45ms · UTC 13:46 · PVG 21:46 · LAX 06:46 · JFK 09:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.