【问题标题】:Why my java long running Threads (5k+ thread) not utilizing all machines cores (12 cores)?为什么我的 java 长时间运行的线程(5k+ 线程)没有利用所有机器内核(12 核)?
【发布时间】:2018-07-20 22:45:18
【问题描述】:

我写了一个简单的多线程 java 应用程序,main 方法只创建了 5k 个线程,每个线程将遍历一个有 5M 记录的列表来处理。

我的机器规格:

  • CPU核心数:12核
  • 内存:13Gb RAM
  • 操作系统:Debian 64 位

我的 jar 现在正在运行,我使用 hTop 来监控我的应用程序,这是我在运行时可以看到的

这就是我构建线程的方式:

ExecutorService executor = Executors.newCachedThreadPool();
Future<MatchResult> future = executor.submit(() -> {
            Match match = new Match();
            return match.find(this);
        });

Match.class

find(Main main){
// looping over a list of 5M 
// process this values and doing some calculations 
// send the result back to the caller 
// this function has no problem and it just takes a long time to run (~160 min)
}

现在我有一些问题:

1-根据我的理解,如果我有一个多线程进程,它将充分利用我所有的内核直到任务完成,那么为什么工作负载只有0.5左右(只使用了半个内核)?

2- 为什么我的 Java 应用程序在实际运行并填满日志文件时状态为“S”(睡眠)?

3- 为什么我只能看到 5k 中的 2037 个线程正在运行(这个数字实际上比这个少,并且随着时间的推移而增加)

我的目标:利用所有内核并尽可能快地完成所有这 5k+ :)

【问题讨论】:

  • 如果没有看到 Match 中的内容,很难知道...
  • Match 类是一个非常简单的类,它在所有正在运行的线程中运行,我在我的问题中提供了有关该类的一些细节
  • 您说它“实际上正在运行并填满日志文件” - 所有线程都写入同一个文件吗?也许他们正在等待对方完成写作。或者即使他们使用不同的文件,他们也可能在等待日志写入完成时处于休眠状态。 (这只是一个猜测)。
  • 另外,如果你有十二个核心,你为什么要创建数千个线程?只能同时运行十二个。
  • 所有线程都通过 java.util.Logger 记录到同一个文件(不知道这是否是最佳实践)

标签: java multithreading htop


【解决方案1】:

根据我的理解,如果我有一个多线程进程,它将充分利用我的所有内核,直到任务完成。

您的理解不正确。在设计不佳的多线程应用程序中可能无法(全部)使用内核的原因有很多。

那为什么工作量只有0.5左右(只用了半个核心)?

一些可能的原因:

  1. 线程可能已死锁。
  2. 线程可能都在争用一个锁(或少数锁),导致大部分线程处于等待状态。
  3. 线程可能都在等待 I/O;例如从某个数据库中读取记录。

这些只是一些更明显的可能原因。

鉴于您的线程正在取得一些进展,我认为解释 #2 非常适合您的“症状”。


不管怎样,创建 5k 个线程几乎可以肯定是一个非常糟糕的主意。其中最多有 12 个可能随时运行。其余的将等待运行(假设您解决了导致线程不足的问题)并占用内存。后者具有各种次要性能影响。

我的目标:利用所有内核并尽可能快地完成所有这 5k+ :)

这两个目标可能互斥 :-)


所有线程都通过 java.util.Logger 记录到同一个文件。

这可能导致他们都在记录器框架中的某个东西上争夺同一个锁。或者日志文件的文件 I/O 出现瓶颈。

一般来说,日志记录是昂贵的。如果您想要提高性能,请尽量减少日志记录,并且对于必须进行日志记录的情况,请使用不会引入并发瓶颈的日志记录框架。


解决此问题的最佳方法是分析代码并找出其大部分时间花费在哪里。

猜测是低效的。

【讨论】:

  • 感谢您的回答 :),实际上线程所做的只是处理一些数据,没有任何代码可以从/在 DB 中读取/写入,它只是处理,
  • 但是您的线程都在将日志消息写入日志文件。这意味着您仍然存在潜在的 I/O 瓶颈。
  • 好吧,我有 100K 男性和 100K 女性,我需要为每个女性找到最佳匹配,所以每个女孩将循环通过 100k 男性为她找到最佳匹配,所以要运行这个我使用线程更快,每个线程只需要 20 个女性,所以 100k/20 = 5k 个线程,所以每个线程将处理 5 个女孩对 100k 个男性(5 X 100k),当然我需要使用日志到文件否则几乎不可能追踪所有这些正在运行的线程,那么如果你是我,你会怎么做? :)
  • 我已经回答过了。我会分析代码并尝试理解它的并发属性。然后,根据我学到的知识,我会修改……或重新设计……以消除瓶颈。
  • 我有一个问题:如果 5k 线程正在调用一个公共静态函数,这可能被认为是锁定吗?假设这个函数只是做了一个从 1 到十亿的 for 循环
【解决方案2】:

谢谢你们,我已经解决了这个问题,现在我可以让 12 个内核运行到最大,如图所示。 :)

我实际上尝试运行此命令jstack &lt;Pid&gt; 以查看我在此进程 ID 中所有正在运行的线程的状态,我发现我的 95% 的线程实际上在 logging 行被 BLOCKED,我做了一些谷歌搜索发现我可以在log4J中使用AsynchAppender所以日志不会阻塞线程

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 2014-01-15
    • 2011-06-28
    • 1970-01-01
    • 2014-03-12
    • 2011-02-21
    相关资源
    最近更新 更多