什么原因导致多线程程序在切换线程时挂起?

时间:2013-01-01 08:31:18

标签: java multithreading

我有一个多线程程序,其线程编号可以自定义。该程序负责生成HTTP请求,将请求发送到Web服务,接收响应并解析响应以获得一些结果值。

由于每个请求获取响应需要将近1秒,因此要使程序获得尽可能多的响应,将启动多个线程。

以下代码用于启动多线程:

    ...
    for (int i = 0; i < threadNum; i++) {
        threadArray[i] = new Thread(requestGeneratorArray[i]);
        threadArray[i].start();
    }

    for (int i = 0; i < threadNum; i++) {
        try {
            threadArray[i].join();
        } catch (InterruptedException e) {
            logger.error(e);
        }
    }

...

当线程数为50,并且生成并发送了总共10K的请求时,程序运行良好。当线程数仍为50时,总请求数为100K。当发送95K +请求时,程序挂起。没有例外,程序只是挂在那里。

然后我添加了一些像这样的JVM参数:java -Xmx1024M -Xms512M -XX:MaxPermSize = 256m ...使用这样的参数,50个线程/ 100K请求工作。但是,50个线程/ 1M请求再次挂起。我将线程号设置为20并将请求号设置为1M,它再次起作用。

我想将线程数设置为50,因为经过测试的请求数量(10K)较少,50个线程的效率最高。请求编号可能会大得多,分别为10M,100M,事件1B。在这种情况下,增加-Xmx -Xms或MaxPermSize的大小不是一个好主意。我该怎么办?程序挂起的根本原因是什么?

=============================================== ============

我直接使用Executor Service代替线程。但问题也出现了。我重新检查了代码,发现有一个错误:我为每个请求实例化了一个HttpClient对象。我更改了代码,为每个线程实例化了一个HttpClient实例,程序不再挂起。

我认为程序挂起的根本原因是它设置了太多与Web服务的HTTP连接,并用尽了Web服务的所有线程。这使得Web服务无法应答任何新到达的请求。这是对的吗?

3 个答案:

答案 0 :(得分:1)

很难仅从这些信息中说出来,但是由于您的堆设置会影响结果,我的赌注将是内容生成和内容解析(存储)之间的调度不佳。

此类应用程序中的一个常见情况是生成内容的线程生成的速度比获取该内容并将其存储的线程更快。这将逐渐增加用于保存内存中内容的堆内存量,并且在某些时候吞吐量将开始直线下降。

首先要做的是通过附加像VisualVM这样的堆查看器来确认这个假设。如果你的堆使用量逐渐增加并且开始在高水平挂钩并且你的吞吐量减少,这可能是罪魁祸首(你可以也确认你内存中的东西确实是生成的内容)。

通常,瓶颈是用于存储内容的持久层的IO。您可以在解析代码(或其他地方)中存在CPU瓶颈,具体取决于您的代码正在执行的操作,但这通常很少见。

这种情况最常见的补救措施是使用有界队列使生成过程等待解析(存储)过程赶上。看看这个SO答案:How to make ThreadPoolExecutor's submit() method block if it is saturated?。您将不得不了解线程池,但它确实是对原始线程的巨大改进,并且它是处理此类问题的最干净的方法。

答案 1 :(得分:1)

+1使用Executor Service的所有建议。

您还提到过您正在使用Http Client。配置参数很少make http client faster for concurrent usage

关于程序挂起。它可能是大型垃圾收集器运行的死锁。我相信像jconsoleVisual VM这样的工具可以帮助您调试这两种情况。

答案 2 :(得分:1)

因为我们不知道线程挂起的位置:您是否考虑过某种线程监视,获取线程的定期堆栈跟踪,使用分析器或使用ThreadMXBeans? 正如其他一些海报所提到的,对于任何可扩展性问题,你也应该关注gc.log。并观察你的记忆足迹。但这可能不是问题,因为即使一个完整的gc最终应该完成,而你的程序却没有。