福哥答案2020-10-26:

简单回答:
CPU密集型:【cpu核心数】【cpu核心数+1】【cpu核心数-1】。
IO密集型:【cpu核心数*2】。
混合型:【cpu核心数 / (1 - 阻塞系数)】,阻塞系数=阻塞时间/(阻塞时间+计算时间)。
求并发:【并发数=线程数/单个任务时间】。

中级回答:
首先,考虑线程池究竟需要几个呢?不同业务是否需要不同线程池来避免某个业务阻塞时,其他业务也无法运行。最好是业务分类,不同的线程池去执行。

N-1原因:然后,每个线程池的线程数量,要考虑业务上下游,cpu,io资源使用的情况,来设计。很多线程池设计为cpu核数-1,例如Java 8之后jvm启动时默认会启动的coomonForkJoinPool,这个线程池执行forkjointask,高峰时很容易吃满cpu,属于计算密集型,这个情况下,最好设置为cpu核数-1,避免出问题时吃满cpu,导致其他业务完全无法运行,并且无法恢复以及定位问题。还有很多线程池设置为cpu核数*2,这是考虑IO是阻塞有延迟的,属于IO密集型,这样在IO阻塞,并且请求到达之间有延迟,每个线程都能充分运用。还要考虑业务上下游,例如上游业务线程池个数,下游业务线程池个数,还有就是本身能使用的IO资源,例如数据库连接个数等等。

N+1原因:计算密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作。所以N+1确实是一个经验值。

《Java并发编程实践》中的方法:
Ncpu = CPU的数量。
Ucpu = 目标CPU的使用率, 0 <= Ucpu <= 1。
W/C = 等待时间与计算时间的比率。
Nthreads = Ncpu x Ucpu x (1 + W/C)。

《Java虚拟机并发编程》中的方法:
线程数 = CPU可用核心数/(1 - 阻塞系数),其中阻塞系数的取值在0和1之间。阻塞系数=阻塞时间/(阻塞时间+计算时间)。

求并发数:
并发数=线程数/单个任务时间。


【原创】腾讯面试官:线程池要设置多大
2020-10-26:线程池的线程数怎么设置比较好?