Java线程和核心数

时间:2015-10-08 18:22:22

标签: multithreading concurrency cpu-cores

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

如果是这样的话,为什么会这样呢?使用大于cpu核心数的线程有什么含义呢?

1 个答案:

答案 0 :(得分:2)

对于一般来说,知道应用程序应该拥有多少线程与基础计算机所拥有的核心数量相关的问题,您可能无法获得任何确定的答案。

有人可能会争辩说,在PaaS软件设计和/或弹性集群时,任何给定流程的固定数量核心的概念可能会被高估。

仍然是你问题的第一部分:

  

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

这有一个明确的答案,这是一个" no" (再次:作为一般规则)。而且,不久之后,所有创建的线程通常不会同时运行(并且可能更重要的是运行能力),这意味着有机会在此进行优化。

作为对此讨论的支持,我反对两种创建应用程序的方法,你可以称之为“古典"与#34;响应",虽然这不是一个普遍接受的部门。但是,让我们用这个作为支持。

经典应用程序设计

我将其标记为主要依赖于"阻止"每个请求调用和/或"线程"图案。考虑传统的I / O方式(套接字通信,如HTTP或数据库连接,基于硬盘的文件读取......):您的应用程序线程调用某种readwrite方法,通常会触发OS级别调用,该操作会填充操作系统级别的某些设备缓冲区(例如,从磁盘读取)。一旦缓冲区收到足够的数据,操作系统就会向您的java应用程序发出信号,read方法将返回缓冲区中的数据。

操作系统工作的整个时间(通常只是一小部分时间,但与典型的GHz CPU速度相比仍然有一些大的时间),您的Java线程处于状态BLOCKED_WAITING,等待要发出信号的操作系统可以恢复。这事儿常常发生。代码分析器工具(如JProfiler或YourKit)可以帮助您测量此时间。如果您这样做,您会注意到在许多进行I / O的应用程序中,这是所谓的#34;壁挂时间的重要组成部分"或者"时钟时间"花了......等等。

所以我们有一个线程在等待,这意味着它没有使用任何CPU时间。它可以被安排出去,操作系统可以自由地为其他人提供CPU时间。

假设这是一个单核CPU,那么现在是另一个线程来提供CPU的好时机。具有两个或多个线程的含义可能是一个很好的设计,即使在单核CPU上也可以最大化CPU使用率,并且可以充分利用硬件。

大多数"经典"如果遵循"每个CPU核心一个线程"的规则,Web应用程序通常会受到这种类型的CPU使用不足,因为Socket通信(或更典型地:等待SQL查询响应所花费的时间)会招致如此多的阻挠 如果你提高你的应用程序拥有的线程数,那么即使一个或两个长时间运行的请求仍然在等待,其他更快的请求将有可运行的线程来运行它们,并且你将获得更好的CPU使用率和更好的性能(数量)并发请求)。

反应式应用设计

识别应用程序的这种典型行为,并使用不同的OS功能集,一些应用程序框架现在使用非阻塞模式(即使对于I / O)来缓解上述问题。 Java生态系统中的示例是基于NIO的网络堆栈,如Netty,或者像Akka这样的actor模式实现。

在典型的"反应性"应用程序,通常会根据请求放弃"线程"我们在经典应用程序中拥有的模式(意味着一个线程负责处理从给定用户请求的开始到结束的所有内容,并在需要外部资源变得可用时等待),支持更大的模块化和 - 阻止方法。

为线程提供了更多技术细微的工作要做,并且每个线程都会将工作交给彼此,并且回调以在完成它们所依赖的工作时收听。这"处理"工作单元意味着每个线程可以快速获取它能够处理的新工作单元。意味着两件事之一:你的应用程序中线程数越来越少,你的CPU使用率越来越高(因为每个人都可以更有效地抓住工作,而不仅仅是坐着等待#34;);或者你可以实例化更多的线程,因为它们主要是等待(不会使CPU饱和),动态切换仍然可以提供良好的CPU使用率。

结论

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

在经典的应用程序设计理念中,这两个数字的关联性比反应性更接近,但我们仍有不同的数据:

  1. 一个非常简单的服务器应用程序可以容纳比CPU核心更多的线程,因为它将允许更好的吞吐量(例如,输出网络带宽的限制)。
  2. 一个SQL重的应用程序,应该被缩减到你的应用程序服务器将使SQL后端饱和的程度。由于您的应用服务器主要等待您的SQL服务器,因此这是限制
  3. 由一些SQL繁重的工作和一些轻量级工作组成的混合应用程序需要进行精确调整,因为您不希望卡住的线程(那些被阻塞等待数据库的线程)匮乏将要服务的轻量级请求更快
  4. 一个计算密集型程序(比如一个加密服务)可能会受益于接近CPU内核数量的许多线程(如果你的算法以经典的方式实现),因为创建的线程比你能够运行的多毫无意义。在基于actor的实现中,创建更多线程实际上可能是一场胜利。