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

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

但是随着时间的推移,我观察到每个Callable(解析/存储)花费的时间越来越长。以这张图片为例,有时完成一个Callable需要10秒或更长时间(绿色条正在运行,紫色条正在等待)。我的电脑停机了,一切都变得迟钝。
这是我的主要算法:
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:
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秒)。我不介意它需要更长的时间,我只希望它需要固定的时间(而不是越来越长)来完成一项工作。
我想知道我是否在解析器中创建了许多(非线程安全的) GregorianCalendar、Date和SimpleDateFormat对象,并导致了一些线程问题。但是我没有重用这些对象,也没有在线程之间传递它们。所以我还是找不到原因。
发布于 2013-12-18 03:28:17
据我所知,如果你有40个页面,每个页面有大约300个URL,你将创建大约12,000个可调用的页面?虽然这可能不是太多的可调用,但它是大量的HTTPConnections和数据库连接。
我认为你应该尝试在每个页面上使用一个Callable。通过并行运行它们,您仍然可以获得大量收益。我不知道您对HTTP请求使用的是什么,但是您也许能够重用那里的系统资源,而不是打开和关闭12,000个系统资源。
尤其是对DB来说。你将只有40个连接。您甚至可以通过在本地收集约300条记录,然后使用批量更新来提高效率。
https://stackoverflow.com/questions/20625792
复制相似问题