服务器上的多线程应用程序比单线程更慢(与JUnit测试不同)

时间:2011-04-08 11:11:29

标签: java multithreading performance debugging

我已将我的应用程序从单线程切换到多线程例程。

这在JUnit测试中非常好用。使用10个线程运行它时,测试需要195 ms才能完成,当只使用一个线程运行它时,应用程序需要406 ms才能完成。所以显然有一个性能优势。

但是当在服务器上运行它时,应用程序现在需要比仅使用单线程时更长的时间。

基本上,我的应用程序在csv文件中读取一行,将其中一个值放入一个集合中,然后将该行打印到另一个文件中。 JUnit测试中输入文件的大小约为35行,服务器上的行大约为6 000 000行。

放置这些值的集合是同步的HashSet,可以包含Long个对象。

我正在使用Java VisualVM监视我的应用程序,但不幸的是我不知道该寻找什么。

对于如何解决这场性能危机,您有什么提示吗?


P上。 S。:大部分时间我的线程被标记为等待,但我不知道它们是否真的在等待,或者它们是否太快而无法让Java VisualVM显示它。


进一步阐明我的例程:我读取单线程文件,但是一旦读取了行,我将生成的对象传递给Runnable,将其放入一个集合并将其打印到另一个文件中。同时读取下一行并传递给其他线程。


正如我在日志文件中看到的那样,线程正在做一些事情而不仅仅是等待。但是有一些,比100 ms更长的时间没有发生。


其中一个跳转:

2011-04-08 12:27:16,580 DEBUG [Thread-10]  runnables.Runner - 7070927
2011-04-08 12:27:16,580 DEBUG [Thread-10]  runnables.Runner - 9058759
2011-04-08 12:27:16,580 DEBUG [Thread-10]  runnables.Runner - 7030928
2011-04-08 12:27:16,580 DEBUG [Thread-10]  runnables.Runner - 15301035
2011-04-08 12:27:16,684 DEBUG [Thread-10]  runnables.Runner - 7700929
2011-04-08 12:27:16,684 DEBUG [Thread-10]  runnables.Runner - 17116545
2011-04-08 12:27:16,685 DEBUG [Thread-10]  runnables.Runner - 4933581
2011-04-08 12:27:16,685 DEBUG [Thread-10]  runnables.Runner - 2861116

注意:当时没有GC发生。


如下面的评论中所写:我正在使用线程池。我的线程在同一个输出文件上打*。他们都写入synchronized方法。


即使我将胎面池的尺寸减小到一个,性能仍然很糟糕。没有比以前的实现更好的了。这不会排除IO依赖或线程切换等事情吗?


我现在修改了我的代码,以便在Runnable内几乎什么都没做。没有Set,没有写作。只有一个日志声明。我仍然得到那些jumps。  所以我排除了一些人提出的写作或Set问题。当只运行一个线程时,我也得到了这些空闲时间。所以线程switiching似乎也不是问题。

3 个答案:

答案 0 :(得分:7)

您的测试文件非常小,因此可能完全由整个I / O堆栈中的任何预读层读取。这使整个执行CPU受到限制。使用更多线程,您可以使用更多CPU并更快地完成它。

真实文件OTOH要长得多,所以问题就变成了IO限制。 CPU大部分时间都在等待读取数据。在单个线程上,没有争用,可能IO更线性;而多线程版本更有可能产生大量的光盘搜索(到目前为止,你可以在今天的硬件上进行最慢的操作)

根据经验,如果您从光盘或网络读取数据并且不对其进行大量处理,最好采用单线程。

答案 1 :(得分:1)

你正在努力的“跳跃”是线程之间的切换时间。由于总体执行时间有限,因此一个线程的执行时间越短,您拥有的线程就越多。如果你有许多线程,你的调度程序最终会切换线程,没有任何线程可以工作。从一个线程切换到另一个线程需要花费一定的时间。如果您的线程不使用多个内核并执行完全相同的操作,那么在将多线程与单线程进行比较时,最终会以更快的速度运行。

答案 2 :(得分:0)

我不知道究竟是什么问题,但似乎是由Executor接口的错误实现引起的。

我现在正在使用

ExecutorService executor = Executors.newFixedThreadPool(nThreads);

一切正常。

  • 单线程例程的持续时间: 17.12分钟
  • 10线程例程的持续时间: 13.45分钟

我发现了一段糟糕的代码:

Thread.sleep(100);

在线程队列已满时调用。