java - ExecutorService 变慢,使我的电脑陷入困境

标签 java executorservice java.util.concurrent

我正在为一个网站编写解析器,它有很多页面(我称它们为 IndexPages)。每个页面都有很多链接(一个 IndexPage 中大约有 300 到 400 个链接)。我使用 Java 的 ExecutorService 在一个 IndexPage 中同时调用 12 个 Callables。每个 Callable 只是向一个链接发出一个 http 请求,并执行一些解析和数据库存储操作。当第一个 IndexPage 完成时,程序继续到第二个 IndexPage,直到找不到下一个 IndexPage。

parsing 1st page 运行时,似乎还可以,我可以很好地观察线程的工作/调度。每个链接的解析/存储只需要大约 1 到 2 秒。

After running 2 hours 但随着时间的推移,我观察到每个 Callable(解析/存储)花费的时间越来越长。以这张照片为例,有时需要 10 秒或更长时间才能完成 Callable(绿色条为 RUNNING,紫色条为 WAITING)。我的 PC 停滞不前,一切都变得迟钝。

这是我的主要算法:

ExecutorService executorService = Executors.newFixedThreadPool(12);    
String indexUrl = // Set initial (1st page) IndexPage
while(true)
{
  String nextPage = // parse next page in the indexUrl

  Set<Callable<Void>> callables = new HashSet<>();
  for(String url : getUrls(indexUrl))
  {
    Callable callable = new ParserCallable(url , … and some DAOs);
    callables.add(callable);
  } 

  try {
    executorService.invokeAll(callables);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }

  if (nextPage == null) 
    break;

  indexUrl = nextPage;
} // true
executorService.shutdown();

该算法简单且不言自明。我想知道是什么导致了这种情况?无论如何要防止这种性能下降?

CPU/Memory CPU/内存/堆显示合理使用。

Environments 环境,仅供引用。

====================更新了====================

我已将我的实现从 ExecutorService 更改为 ForkJoinPool :

ForkJoinPool pool=new ForkJoinPool(12);
String indexUrl = // Set initial (1st page) IndexPage
while(true)
{
  Set<Callable<Void>> callables = new HashSet<>();
  for(String url : for(String url : getUrls(indexUrl)))
  {
    Callable callable = new ParserCallable(url , DAOs...);
    callables.add(callable);
  }
  pool.invokeAll(callables);

  String nextPage = // parse next page in this indexUrl
  if (nextPage == null)
    break;

  indexUrl = nextPage;
} // true

它比 ExecutorService 的解决方案花费的时间更长。 ExecutorService 完成所有页面大约需要 2 小时,而 ForkJoinPool 需要 3 小时,并且每个 Callable 仍然需要越来越长的时间来完成(从 1 秒到 5,6 或甚至 10 秒)。我不介意它需要更长的时间,我只希望完成一项工作需要恒定的时间(不是越来越长)。

我想知道是否在解析器中创建了很多(非线程安全的)GregorianCalendarDateSimpleDateFormat 对象导致一些线程问题。但我没有重用这些对象或在线程之间传递它们。所以我还是找不到原因。

最佳答案

根据堆,您有内存问题。 ExecutorService.invokeAllCallable 实例的所有结果收集到一个 List 中,并在它们全部返回时返回该 List完全的。您可能想考虑简单地调用 ExecutorService.submit,因为您似乎并不关心每个 Callable 的结果。

关于java - ExecutorService 变慢,使我的电脑陷入困境,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20625792/

相关文章:

java - 优雅的非线性任务调度?

java - 是否有一个 java.util.concurrent 类允许定期启动线程而不阻塞完成?

java - Java 中的按键阻塞映射

java - 简单的 Java 应用程序可以与 Ant 配合使用,但不能与 Maven 配合使用。我该怎么做才能纠正这个问题?

java - 使用 ExecutorService 在具有通过循环传递的不同参数的类中同时执行方法时出现问题

java - Spring webflux WebClient日志 'Connection reset by peer'

java - 如何访问自定义线程池执行器方法中的线程对象?

java - 多线程应用程序中的Java垃圾回收以获取局部变量

java - 每个线程都有一个堆栈空间吗?

java - 错误 : Could not find or load main class welcome. to.java.WelcomeToJava