逻辑核心数
一般百度如何获取电脑的逻辑核心数,都会出现这段代码:
Runtime.getRuntime().availableProcessors()
然而,问题在于Runtime.getRuntime().availableProcessors()也并非都能返回你所期望的数值。比如说,在我的双核1-2-1机器上,它返回的是2,这是对的。不过在我的1-4-2(1个物理处理器-4个核-每个核2个超线程=也就是通常说的4核8线程)机器 上,也就是一个CPU插槽,4核,每个核2个超线程,这样的话会返回8。不过我其实只有4个核,如果代码的瓶颈是在CPU这块的话,我会有7个线程在同时 竞争CPU周期,而不是更合理的4个线程。如果我的瓶颈是在内存这的话,那这个测试我可以获得7倍的性能提升。
不过这还没完!Java Champions上的一个哥们发现了一种情况,他有一台16-4-2的机器 (也就是16个CPU插槽,每个CPU4个核,每核两个超线程,返回的值居然是16!从我的i7 Macbook pro上的结果来看,我觉得应该返回的是1642=128。在这台机器上运行Java 8的话,它只会将通用的FJ池的并发数设置成15。正如 Brian Goetz所指出的,“虚拟机其实不清楚什么是处理器,它只是去请求操作系统返回一个值。同样的,操作系统也不知道怎么回事,它是去问的硬件设备。硬件会告诉它一个值,通常来说是硬件线程数。操作系统相信硬件说的,而虚拟机又相信操作系统说的。”
所以不同业务下的情况不尽相同,不能一概而论都根据核心数量去统一配置线程池。
那么如果我们线程池主要用于处理 IO 密集型的任务时,如读取文件,数据库连接,网络通讯等,我们都知道 IO 读写的速度,与CPU相比的话,肯定是慢的多,所以一般建议去机器逻辑核心数的 2 倍。
多个线程池
一般项目可能会创建多个线程池,因为比如2种业务,一种要求不高,但是数量极多,可以重试,一种是数量小,要求及时,如果放在一起,会因为多的数量可能影响你那笔少的,
线程池用来管理线程,一般需要一个定时线程池,一个业务线程池,一个io线程池,有的时候,定时线程池和业务线程池也可以共用,比如netty的线程池就可以共用。业务线程池用于处理核心业务,一般要求速度快,延迟低,而io线程池一般用于网络交互,比如数据库操作,rpc请求,或者磁盘读写。
业务场景
主要要先看我们线程池用于执行的任务性质。如果该线程池主要用于处理计算密集型的任务时,如加密、大数分解等主要利用CPU资源的任务,一般建议将我们的线程数设置为机器的核心数(逻辑核心)+ 1
这里为什么要进行加一呢?这是为了避免我们CPU会出现页缺失的情况(页缺失是指线程执行所需的数据还未来来的及加载,部分还存在于磁盘之上,那么这个线程就会被挂起。)
那么如果我们线程池主要用于处理 IO 密集型的任务时,如读取文件,数据库连接,网络通讯等,我们都知道 IO 读写的速度,与CPU相比的话,肯定是慢的多,所以一般建议去机器逻辑核心数的 2 倍。
另外如果我们线程池既处理计算密集型任务,也处理IO密集型任务时。那么我们应该如何处理?
IO 密集型所花费的时间远大于计算密集型花费的时间,拆分意义不大,这时就没有必要拆分;但是如果 IO 密集型花费的时间约等于计算密集型的时间,则建议将其拆分开来。