解决方法
几件事情是无可争辩的真实:
>更多线程意味着更多的内存使用.每个线程需要一个线程栈.对于最近的HotSpot JVM,最小线程堆栈大小为64Kb,默认值可以高达1Mb.这可能是重要的.此外,任何活着的线程都可能拥有或共享堆中的对象,无论它当前是否可运行.因此,合理的是期望更多的线程意味着更大的存储器工作集.
>执行硬件上的核心(或超线程内核或其他任何内核),JVM不能实际运行更多的线程.一辆汽车不会在没有发动机的情况下运行,并且一个线程不会运行没有一个核心.
除此之外,事情变得不那么明确. “问题”是一个活动的线程可以在各种“状态”.例如:
活动线程可以运行;即主动执行指令.
活动线程可以运行;即等待一个核心,以便它可以运行.
活动线程可以通过同步;即等待来自另一线程的信号,或等待释放锁.
活动线程可以等待外部事件;例如等待一些外部服务器/服务来响应请求.
“每个核心一个线程”启发式假设线程正在运行或可运行(根据上述).但是对于很多多线程应用程序,启发式是错误的…因为它不考虑其他状态的线程.
现在“太多”线程明显可以导致显着的性能下降,通过使用太多内存简单. (想象一下,您拥有4Gb的物理内存,并创建8,000个线程,具有1Mb堆栈,这是虚拟内存颠覆的一个方法.)
但是其他的东西呢线程太多会导致上下文切换过多?
我不这么认为如果您有很多线程,并且您的应用程序使用这些线程可能会导致过多的上下文切换,并且这对于性能不利.然而,我认为上下文切换的根本原因不是实际的线程数.性能问题的根源更可能是应用程序:
>以特别浪费的方式同步;例如当Object.notify()会更好的时候使用Object.notifyAll()
>在高度竞争的数据结构上同步OR
>相对于每个线程正在做的有用的工作量,做太多的同步
>试图做太多的I / O并行.
(在最后一种情况下,瓶颈可能是I / O系统,而不是上下文切换…除非I / O是同一机器上具有服务/程序的IPC).
另一方面,在上述不存在混淆因素的情况下,多线程不会增加上下文切换.如果您的应用程序有N个可运行的线程竞争M个处理器,并且线程纯粹是计算和无竞争的,那么操作系统的线程调度程序将尝试在它们之间进行时间分片.但是,时间片长度可能会在十分之一秒(或更多)内测量,因此与cpu绑定线程在其切片中实际执行的工作相比,上下文切换开销可以忽略不计.如果我们假设时间片的长度是恒定的,那么上下文切换开销也将是恒定的.添加更多可运行的线程(增加N)不会显着地改变工作与开销的比率.
总而言之,“太多线程”对于性能是有害的.然而,实际上有多少“太多”,没有一个普遍的“经验法则”. (幸运的是),在性能问题变得显着之前,您通常有相当大的余地.