multithreading - Java线程和内核数

标签 multithreading concurrency cpu-cores

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

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

最佳答案

通常来说,相对于底层计算机具有的内核数量,您可能不会得到任何明确的答案,即了解应用程序应具有多少个线程。

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

不过,您的问题的第一部分是:

Is it recommended that the number of threads in a java application should be less than the number of cpu cores?



这有一个明确的答案,这是一个“否”(通常:更多)。简短地说,所有创建的线程通常都不会立即运行(也许更重要的是,可以运行能力)的原因,这意味着有机会在此处进行优化。

为了支持此讨论,我将反对两种创建应用程序的方法,尽管这不是通常可以接受的划分,但您可以将其称为“经典”与“响应式(Reactive)”。但是,让我们以此作为支持。

古典应用设计

我将其标记为经典应用程序,这些应用程序主要依赖于“阻塞”调用和/或“每个请求线程”模式。考虑完成I/O的传统方式(HTTP或数据库连接之类的套接字通信,基于硬盘的文件读取等):您的应用程序线程会调用某种readwrite方法,通常会触发操作系统级别的调用,阻止您的应用程序线程,并在操作系统级别填充一些设备缓冲区(例如,从磁盘读取)。缓冲区接收到足够的数据后,操作系统会向您的Java应用程序和线程发出信号以恢复 Activity ,并且read方法返回缓冲区中的数据。

在操作系统运行的整个过程中(通常只有几分之一秒,但与典型的GHz CPU速度相比仍要花费很多时间),您的Java线程处于BLOCKED_WAITING状态,等待操作系统发出可以恢复运行的信号。这事儿常常发生。诸如JProfiler或YourKit之类的代码探查器工具可以帮助您衡量这一时间。如果这样做,您会注意到,在许多执行I/O的应用程序中,这是花费在等待中的所谓“挂墙时间”或“时钟时间”的重要组成部分。

因此,我们有一个线程在等待,这意味着它不占用任何CPU时间。可以计划它,并且操作系统可以自由地将CPU时间分配给其他任何人。

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

如果您遵循“每个CPU内核一个线程”的规则,则大多数“经典” Web应用程序通常会遭受这种类型的CPU使用不足,因为套接字通信(或更常见的是:WAITING对SQL查询的响应所花费的时间)将造成了如此多的阻碍。
如果您增加了应用程序具有的线程数,那么即使一个或两个长时间运行的请求仍在等待,其他更快的请求也将具有可运行的线程来运行它们,您将获得更好的CPU使用率和更好的性能(并发数量)要求)。那就是...直到其他东西达到饱和为止(您的数据库上有太多繁重的请求,太多的同时硬盘读取/写入...)

响应式应用程序设计

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

在典型的“响应式”应用程序中,通常会放弃传统应用程序中的“每个请求线程”模式(这意味着一个线程负责处理给定用户请求的从头到尾的所有事务,并在需要外部请求时等待)资源可用),以支持模块化程度更高且非阻塞的方法。

线程被分配了更多的技术工作要做,每个线程将把工作移交给其他人,并在完成它们所依赖的工作时回调以进行监听。工作单元的这种“处理”意味着每个线程都可以快速获取其能够处理的新工作单元。这意味着两件事之一:您可以在应用中使用更少的线程来提高CPU使用率(因为每个线程可以更有效地抓取工作,而不仅仅是坐在“等待”中);或者您可以实例化更多的线程,因为它们通常都在等待(不会使CPU饱和),并且动态切换仍将允许良好的CPU使用率。

结论

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

在经典的应用程序设计理念上,这两个数字比在响应式上更紧密相关,但是,我们仍然有不同的特征:
  • 一个非常简单的服务器应用程序可以容纳比CPU内核更多的线程,因为它可以提供更好的吞吐量(限制是输出网络带宽)。
  • 是SQL繁重的应用程序,应调用到您的应用程序服务器将使SQL后端饱和的程度。由于您的应用服务器将主要在等待您的SQL服务器,因此这是
  • 的限制
  • 一个混合的应用程序,其中包含一些SQL繁重的工作和一些轻量级的工作,需要进行精确调整,因为您不希望卡住的线程(那些阻塞等待DB的线程)饿死了那些可以更快地得到响应的轻量级请求
  • 一个计算密集型程序(例如,加密服务)可能会受益于接近CPU内核数的多个线程(如果您的算法以经典方式实现),因为创建的线程数超出了您的运行能力,这是没有意义的。在基于actor的实现中,创建更多线程实际上可能是一个胜利。
  • 关于multithreading - Java线程和内核数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33023238/

    相关文章:

    java - 修改 JVM 以序列化跨线程的文件访问

    java - JPanel添加但不显示 "in time"

    database - 并发数据库事务

    java - 应该从事件调度程序还是主线程控制 Swing GUI 应用程序?

    python - Python 3.6 中的线程不提供输出

    java - 使用方法调用的引用分配中的线程安全问题

    concurrency - 单核 CPU 能否实现真正的并发?

    r - 检测 R 中的可用和空闲内核

    performance - 可能使用并发/异步/并行方法比较 2 个数据集

    multithreading - 多个 cpu、多个核心和线程数