Skip to content

Latest commit

 

History

History
17 lines (13 loc) · 3.39 KB

File metadata and controls

17 lines (13 loc) · 3.39 KB

#netty4的workgroup默认线程个数为什么是Runtime.getRuntime().availableProcessors()*2

###前记   前几天去参加一个内部转岗的面试,在谈到netty的时候,我讲到netty处理OP_READ,OP_WRITE的work group默认线程数是cpu可用核数乘以2,当时面试官问我:为什么这个线程数默认是cpu可用核数*2?我当时没有回答上来。
  面对这个问题,虽然大家下意识的都认为创建**"cpu可用核数乘以2线程个数"**(其实这个说法是不准确的,后续再解释)的目的一定是为了充分利用CPU同时减少CPU的上下文切换开销,但是到底为什么这个线程数可以达到这个效果,估计很少人可以说出一个之所以然。
  我回来以后也一直纠结于这个问题,查找了相关资料,然后偶然从《Java性能优化权威指南》这本书上看到了相关解释,以此记录下来,以供自己以后学习和探讨。

###CPU的架构 回答这个问题之前我们先了解一下CPU和OS相关的知识:
  现代CPU处理器引入了芯片多处理器和芯片多线程,也就是每核多硬件线程(Runnable Hardware Thread),以应对CPU高速缓存未命中所带来的问题。有的CPU有4,6或者8个核,每核有4个硬件线程。从操作系统的角度来看,8核的CPU就像有32个处理器即操作系统把每个核中每个硬件线程看成一个处理器。   在每个核有4个线程的CPU中,在一个时钟周期内,每核4个硬件线程中只有一个可以运行。发生延迟时,例如CPU高速缓存未命中,如果同一个核中还有其他就绪的硬件线程,下一个时钟周期就会让这个硬件线程运行,这样与那些每核单线程的CPU相比,这种CPU就会被诸如CPU高速缓存未命中这样的长延迟事件所阻塞,从而因等待事件完成而浪费时钟周期。对于这类CPU,如果就绪的应用线程已经准备好运行而没有可用的硬件线程,运行前就必须进行线程上下文切换。线程上下文通常会耗费数百个时钟周期。

###CPU的调度队列   CPU在处理过程中,有一个CPU调度程序的运行队列。运行队列中就是那些已准备好运行、正等待可用CPU的轻量级进程。如果准备运行的轻量级线程数超过系统所能处理的上限,运行队列就会很长。运行队列长表明系统负载可能已经饱和。系统运行队列长度等于虚拟处理器(系统硬件线程)个数时,用户不会明显感觉到性能下降,也就是我们使用java的Runtime.getRuntime().availableProcessors()的返回值,所以这里返回并不是cpu核数而是CPU的虚拟处理器(系统硬件线程)的个数。当运行队列长度达到虚拟处理器的4倍或者更多时,系统响应就非常迟缓了。

###问题的回答   所以,综上所述,假设我们使用netty4.0.x开发的一个应用程序运行在一个4核CPU,每个CPU有4个硬件线程,那么实际的CPU虚拟处理器(硬件线程)是4乘以4=16,即Runtime.getRuntime().availableProcessors()的返回值是16,那么netty默认创建的work group的应用线程个数是16乘以2=32,所以我们假设的硬件线程完全跑满,那么则是耗费16个应用线程,那么还有16个应用线程则正好在CPU的调度运行队列,正好等于虚拟处理器的个数,也达到性能最高。