首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ExecutorService变慢了,我的电脑瘫痪了

ExecutorService变慢了,我的电脑瘫痪了
EN

Stack Overflow用户
提问于 2013-12-17 12:11:56
回答 3查看 559关注 0票数 4

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

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

但是随着时间的推移,我观察到每个Callable(解析/存储)花费的时间越来越长。以这张图片为例,有时完成一个Callable需要10秒或更长时间(绿色条正在运行,紫色条正在等待)。我的电脑停机了,一切都变得迟钝。

这是我的主要算法:

代码语言:javascript
运行
复制
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/Heap显示合理的使用。

环境,仅供参考。

====================已更新====================

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

代码语言:javascript
运行
复制
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对象,并导致了一些线程问题。但是我没有重用这些对象,也没有在线程之间传递它们。所以我还是找不到原因。

EN

回答 3

Stack Overflow用户

发布于 2013-12-17 12:20:42

基于堆,你有一个内存问题。ExecutorService.invokeAllCallable实例的所有结果收集到一个List中,并在所有结果完成时返回该List。您可能希望考虑简单地调用ExecutorService.submit,因为您似乎并不关心每个Callable的结果。

票数 1
EN

Stack Overflow用户

发布于 2013-12-17 12:57:26

我不明白为什么需要Callable来解析你的索引页,因为你的'Caller‘方法不需要来自ParserCallable的任何结果。我可以看到你需要位异常处理,但它仍然可以用Runnable来管理。

当您使用Callable.call()时,它将返回从未使用过的FutureTask。

您应该能够通过使用Runnable来改进实现,这可以避免这种额外的操作

代码语言:javascript
运行
复制
ExecutorService executor = Executors.newFixedThreadPool(12);
for(String url : getUrls(indexUrl))  {
  Runnable worker = new ParserRunnable(url , … and some DAOs);
  executor.execute(worker);
}

class ParserRunnable implements Runnable{
}
票数 0
EN

Stack Overflow用户

发布于 2013-12-18 03:28:17

据我所知,如果你有40个页面,每个页面有大约300个URL,你将创建大约12,000个可调用的页面?虽然这可能不是太多的可调用,但它是大量的HTTPConnections和数据库连接。

我认为你应该尝试在每个页面上使用一个Callable。通过并行运行它们,您仍然可以获得大量收益。我不知道您对HTTP请求使用的是什么,但是您也许能够重用那里的系统资源,而不是打开和关闭12,000个系统资源。

尤其是对DB来说。你将只有40个连接。您甚至可以通过在本地收集约300条记录,然后使用批量更新来提高效率。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20625792

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档