线程池中的代码比没有线程的运行慢得多

时间:2012-05-15 15:29:35

标签: java multithreading zip threadpool

我有一些代码,它读取一组二进制文件,这些文件基本上由许多序列化的java对象组成。我正在尝试通过运行线程池中的文件读取来并行化代码(Executors.newFixedThreadPool

我所看到的是,在线程化时,读取的运行速度实际上比在单个线程中慢 - 从慢到1.5倍,这取决于线程的数量。

在我的测试用例中,我实际上是从多个线程中读取相同的文件(35mb),所以我不受任何方式的I / O约束。我没有运行比CPU更多的线程,并且我没有池之间的任何同步 - 即我只是独立处理一堆文件。

有没有人知道在线程化时这种性能下降的可能原因是什么?我应该寻找什么?或者解决问题的最佳方法是什么?我已经在类中查找了静态变量,这些变量可以在线程之间共享,但我没有看到任何变量。 在线程中实例化时,其中一个java.*类的运行速度会慢得多(例如我正在使用的java.zip.deflate)? 谢谢你的任何提示。

Upd:另一个有趣的提示是,当一个线程正在运行该函数的执行时间时,该函数的执行时间是高精度的,但是当运行多个线程时,我看到时序的显着变化。

3 个答案:

答案 0 :(得分:2)

听起来像是在期待java.zip.deflate读取35mb以便在添加多个执行相同作业的线程时运行得更快。它不会。实际上,虽然您可能不是IO绑定,但您仍然会在每次添加的线程中产生内核开销 - 缓冲区副本等。即使您完全从内核缓冲区空间读取,也会产生CPU和处理开销。

那就是说,我很惊讶你的速度慢了1.5到10倍。如果每个处理线程都在写输出,那么显然不会被缓存。

但我怀疑您可能会引发内存争用。如果您正在处理Java序列化对象流,则需要观察内存消耗,除非您经常重置它。序列化保留了很多对象的引用,因此大的连续流可以产生大量的GC带宽。

我将使用jconsole连接到您的程序并密切关注内存选项卡。随着幸存者和老一代空间的填充,看到非线性CPU的影响。

答案 1 :(得分:0)

仅仅因为所有线程工作者都从同一个文件读取并不意味着它不是IO绑定的。有可能。它可能不是。确保设置测试用例,以便所有线程工作者都从内存中的文件读取磁盘。

您在上面提到过您认为操作系统已缓存该文件,但您是否确定该文件是以只读/共享模式打开的?如果没有,那么操作系统仍然可以锁定文件以确保一次只有一个线程可以访问。

潜在相关链接:

答案 2 :(得分:0)

问题是由java.util.zip.Inflate类引起的,它实际上有很多同步方法(因为其中有几个使用本机代码),因此当运行多个线程时,同步方法相互竞争,使代码非常接近顺序。

解决方案是用GNU类路径中的仅Java版本替换java.util.zip类(例如从此处http://git.savannah.gnu.org/cgit/classpath.git/tree/java/util/zip