Multithreading Java线程和内核数

Multithreading Java线程和内核数,multithreading,concurrency,cpu-cores,Multithreading,Concurrency,Cpu Cores,是否建议java应用程序中的线程数应小于cpu内核数 如果是这样的话,为什么会是这种情况?使用线程数大于cpu内核数的含义是什么?对于知道一个应用程序应该有多少线程(与底层计算机的内核数相关)这个问题,您可能不会得到任何明确的答案 也有人可能认为,在PaaS软件设计和/或弹性集群时,任何给定过程的固定数量的核心的概念可能被高估了 不过,你问题的第一部分: 是否建议java应用程序中的线程数应小于cpu内核数 这有一个明确的答案,那就是“不”(再一次:作为一般规则)。原因是,很快,所有创建的线程通

是否建议java应用程序中的线程数应小于cpu内核数


如果是这样的话,为什么会是这种情况?使用线程数大于cpu内核数的含义是什么?

对于知道一个应用程序应该有多少线程(与底层计算机的内核数相关)这个问题,您可能不会得到任何明确的答案

也有人可能认为,在PaaS软件设计和/或弹性集群时,任何给定过程的固定数量的核心的概念可能被高估了

不过,你问题的第一部分:

是否建议java应用程序中的线程数应小于cpu内核数

这有一个明确的答案,那就是“不”(再一次:作为一般规则)。原因是,很快,所有创建的线程通常不会同时运行(更重要的是,可能会同时运行able),这意味着这里有一个优化的机会

作为对本次讨论的支持,我将反对两种创建应用程序的方法,你可以称之为“经典”与“反应”,尽管这不是一种普遍可接受的划分。但是,让我们用它作为支持

经典应用程序设计 我将其标记为主要依赖于“阻塞”调用和/或“每个请求线程”模式的经典应用程序。考虑传统的I/O方式(socket通信,如HTTP或数据库连接,基于硬盘的文件读取,……):应用程序线程调用某种代码> >代码>或代码>写/代码>方法,该方法通常触发OS级调用,阻塞您的应用程序线程,在OS级别填充一些设备缓冲区。(比如说,从磁盘读取)。一旦缓冲区接收到足够的数据,操作系统就会向java应用程序和线程发出信号,要求它们恢复活动,
read
方法返回缓冲区中的数据

操作系统工作的整个时间(通常仅为一秒钟的一小部分,但与您典型的GHz CPU速度相比,时间仍然很长),您的Java线程处于状态
阻塞\u等待
,等待操作系统发出它可以恢复的信号。这种情况随时都会发生。代码探查器工具,如JProfiler或YourKit,可以帮助您测量此时间。如果您这样做,您会注意到,在许多进行I/O的应用程序中,这是所谓“墙时间”或“时钟时间”的重要部分那就是…等待

因此,我们有一个线程在等待,这意味着它没有使用任何CPU时间。它可以被调度出去,操作系统可以自由地将CPU时间分配给其他任何人

假设这是一个单核CPU,那么现在正是让另一个线程为CPU供电的好时机。这意味着拥有两个或更多线程可能是一个很好的设计,可以最大限度地提高CPU使用率,即使是在单核CPU上,也可以充分利用硬件

如果遵循“每个CPU核一个线程”的规则,大多数“经典”web应用程序通常会出现这种CPU使用不足的情况,因为套接字通信(或更典型的情况是:等待SQL查询响应所花费的时间)将导致大量阻塞。
如果你提高应用程序的线程数,那么即使有一两个长时间运行的请求仍在等待,其他更快的请求也会有可运行的线程来运行它们,你将获得更好的CPU使用率和更好的性能(并发请求数)。也就是说,直到其他请求达到饱和(数据库上有太多繁重的请求,太多同时进行的硬盘读取/写入…)

反应式应用程序设计 认识到应用程序的这种典型行为,并使用不同的操作系统功能集,一些应用程序框架现在使用非阻塞模式(甚至对于I/O)来缓解上述问题。Java生态系统中的示例包括基于NIO的网络堆栈(如Netty)或演员模式实现(如Akka)

在典型的“反应式”应用程序中,通常会放弃经典应用程序中的“每个请求线程”模式(这意味着一个线程负责处理给定用户请求从头到尾的所有内容,并在需要时等待外部资源可用),支持更模块化、无阻塞的方法

线程被赋予了更多的技术粒度的工作,每个线程将把工作交给另一个线程,并在它们所依赖的工作完成时回调以听取反馈工作单元的数量意味着每个线程可以快速获取它能够处理的新工作单元。这意味着两件事之一:在应用程序中用更少的线程实现更高的CPU使用率(因为每个线程可以更高效地获取工作,而不是仅仅坐在“等待”中);或者你可以实例化更多的线程,因为它们大部分都在等待(不会使CPU饱和),动态切换仍然允许良好的CPU使用率

结论 无论如何,你不能仅仅根据可用内核的数量来设计线程的数量。你的实现和工作的性质决定了要创建的最佳线程的数量

在经典的应用程序设计理念中,这两个数字比反应式数字更为密切相关,但我们仍有不同的特点:

  • 一个非常简单的服务器应用程序可以容纳比CPU核多得多的线程,因为它将允许更好的吞吐量(限制是,比如,输出网络带宽)
  • SQL密集型应用程序应扩展到应用程序服务器将饱和SQL后端的程度。由于应用程序服务器将主要等待SQL服务器,因此这是限制
  • 由一些SQL繁重的工作和一些轻量级的工作组成的混合应用程序将需要精确调整,因为您不希望被卡住的线程(那些等待DB的被阻塞线程)使轻量级的请求无法得到更快的服务
  • 一种计算密集型程序(例如加密服务)