【问题标题】:Why multithread program in Java slow and yet doesn't use much CPU time?为什么 Java 中的多线程程序速度很慢,但占用的 CPU 时间不多?
【发布时间】:2013-07-23 08:54:49
【问题描述】:

我的 Java 程序使用 java.util.concurrent.Executor 来运行多个线程,每个线程都启动一个可运行的类,在该类中它从 C: 驱动器上的逗号分隔文本文件中读取并循环遍历行以拆分和解析将文本转换为浮点数,然后将数据存储到:

static Vector
static ConcurrentSkipListMap

我的电脑是 Win 7 64bit,Intel Core i7,有 6 * 2 核心和 24GB 内存,我注意到程序将运行 2 分钟并完成所有 1700 个文件,但 CPU 使用率只有 10% 左右到 15%,无论我分配多少线程:

Executor executor=Executors.newFixedThreadPool(50);

Executors.newFixedThreadPool(500) 不会有更好的 CPU 使用率或更短的时间来完成任务。没有网络流量,一切都在本地 C: 驱动器上,有足够的内存供更多线程使用,当我将线程增加到 1000 时,它会出现“OutOfMemoryError”。

为什么更多的线程不能转化为更多的 CPU 使用率和更少的处理时间,为什么?

编辑:我的硬盘是 SSD 200 GB。

编辑:终于发现问题出在哪里,每个线程将其结果写入一个由所有线程共享的日志文件,我运行应用程序的次数越多,日志文件越大,它变得越慢,因为它是共享,这肯定会减慢进程,所以在我停止写入日志文件后,它会在 10 秒内完成所有任务!

【问题讨论】:

  • 更多线程不会让你的磁盘更快。
  • 谷歌一些图形CPU时间分析器,并检查CPU在“IO等待”状态下花费了多少时间。这受磁盘(和其他资源)延迟的影响。仅供参考,一个好的 SSD 磁盘可以将 IO 等待减少到几乎为零。
  • 你的线程有同步吗?
  • 同步由 Executor 处理,不是吗?我没有其他事情要做。
  • 如果您的线程访问相同或某些相同的对象,您可能只会进行同步。这可能以使用具有内部同步的对象的形式出现(例如VectorConcurrentSkipListMap您正在使用)或通过synchronized。多个线程是否访问相同的VectorConcurrentSkipListMap

标签: java multithreading cpu-usage


【解决方案1】:

您可能受到 IO 的限制,而不是 cpu。

你能减少打开文件阅读的次数吗?也许打开一次,读取所有行,将它们保存在内存中,然后对其进行迭代。

否则,您将不得不考虑获得更快的硬盘驱动器。 SSD 的速度可能非常快。

【讨论】:

  • 是的,这就是我现在正在做的,只需打开每个文件一次。
【解决方案2】:

有可能您的线程在系统上被赋予了较低的优先级?在这种情况下增加线程数不会增加 CPU 使用率,因为分配给程序的 CPU 空间量可能会在其他地方受到限制。

是否有任何可能发生类似情况的配置文件/初始化步骤?

【讨论】:

  • 我将线程优先级从正常提高到 9,但没有任何作用。
【解决方案3】:

OutOfMemoryError 可能来自 Java 自身对内存使用的限制。尝试使用一些参数here 来增加最大内存。

对于速度,Adam Bliss 从一个很好的建议开始。如果这是一遍又一遍的同一个文件,那么我想有多个线程尝试同时读取它可能会导致文件上的锁发生大量争用。更多的线程甚至意味着更多的争用,这甚至可能导致更差的整体性能。所以避免这种情况,如果可能的话,只需加载一次文件。即使它是一个大文件,你也有 24 GB 的 RAM。您可以保存相当大的文件,但您可能需要增加 JVM 允许的内存以允许加载整个文件。

如果使用了多个文件,请考虑以下事实:您的磁盘一次只能读取一个文件。因此,如果线程没有花费太多时间进行处理,那么让多个线程同时尝试使用磁盘可能不会太有效。由于您的 CPU 使用率很低,因此线程可能会加载文件的一部分,然后在缓冲的部分上运行得非常快,然后花费大量时间等待文件的其余部分加载。如果您一遍又一遍地加载文件,这甚至仍然适用。

简而言之:磁盘 IO 可能是您的罪魁祸首。您需要努力减少它,以便线程不会过多地竞争文件内容。

编辑:

经过进一步考虑,这更有可能是同步问题。尝试添加到结果列表的线程可能会被阻止。如果访问频繁,这将导致对象上的大量锁争用。考虑做一些事情,比如让每个线程将其结果保存在本地列表中(如ArrayList,这是不是线程安全的),然后将所有值以块的形式复制到最终的共享列表中以尝试减少争用。

【讨论】:

  • 每个线程只读取一个文件,没有两个线程会读取同一个文件,我的应用程序只读取每个文件一次。虽然我有一个 SSD,但你有一个很好的观点,它可能无法同时处理多个文件读取,这可能是瓶颈。谢谢!
  • 磁盘根本不读取文件,它读取磁盘块,一次一个。这意味着确实可以同时读取许多文件。
  • @Ingo 从技术上讲,当然。但这是不会改变重点的细节,尤其是对于大量文件。这些文件不太可能挤在一个小的磁盘空间中,我们当然不能假设是这种情况。最后,磁盘一次只能读取一组数据,这意味着任何寻找该块之外的东西的线程都会被 I/O 阻塞。
猜你喜欢
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多