使用ExecutorService并行处理作业

时间:2012-09-24 20:53:06

标签: java web-crawler executorservice threadpoolexecutor

我正在编写一个需要处理大量URL的java程序 每个URL将按顺序运行以下作业:下载,分析,压缩

我希望每个作业都有一个固定数量的线程,以便所有作业在任何给定时间都可以并发运行,而不是让每个URL一次完成所有作业。

例如,下载作业将有多个线程来获取和下载URL,只要下载了一个URL,它就会将其传递给analyze作业中的一个线程,一旦完成,它就会出现传递给压缩作业中的线程等

我正在考虑在java中使用CompletionService,因为它在完成后立即返回结果,但我不确定它是如何工作的,到目前为止我的代码看起来像这样:

ExecutorService executor = Executors.newFixedThreadPool(3);
CompletionService<DownloadedItem> completionService = new ExecutorCompletionService<DownloadedItem>(executor);

//while list has URL do {
   executor.submit(new DownloadJob(list.getNextURL());//submit to queue for download
//}

//while there is URL left do {
   Future<DownloadedItem> downloadedItem = executor.take();//take the result as soon as it finish
   //what to do here??
//}

我的问题是如何将下载的项目移动到分析作业并在那里完成工作而无需等待所有下载作业完成?我正在考虑为每个作业创建一个CompletionService,这是一个可行的方法吗?如果没有,是否有更好的替代方法来解决这个问题?请提供示例。

3 个答案:

答案 0 :(得分:3)

一旦你提到IN ORDER任何尝试为顺序使用单独的线程,任务只会使你的系统设计复杂化。

在我看来,最好的方法是让单独的线程同时处理各个URL。要执行3个步骤,您可以引入另一个抽象(例如使用3个callables),但您仍然希望在一个线程中按顺序执行它们。而且无需完成服务。

答案 1 :(得分:1)

你非常接近。首先将您的任务提交到CompletionService

completionService.submit(new DownloadJob(list.getNextURL());

现在抓住Future并等待它:

DownloadedItem> downloadedItem = executor.take().get();

呼叫get()可能会阻止。重复上面的行数是您提交的项目的数量。


如果您需要更多,更大的吞吐量(在您的情况下,一次最多可下载三个URL),请考虑async-http-client,这样您就可以同时从数千个URL下载。它使用NIO并且是事件驱动的,不涉及线程。

答案 2 :(得分:1)

您所描述的内容称为Pipeline。基本上,下载任务的输出是分析任务的输入。分析的输出是压缩的输入。似乎有两种方法可以实现这一目标:

1)让下载任务了解输出管道,以便它可以自己提交结果。

class DownloadTask implement Runnable {
    Executor analyzePipeline;
    public void run() {
        //Do download stuff
        analyzePipeline.submit(new AnalyzeTask(downloaded content));
    }
}

2)允许另一个线程将结果从下载任务移动到分析任务的管道中。

ExecutorService executor = Executors.newFixedThreadPool(3);
ExecutorService analyzeExecutor = Executors.newFixedThreadPool(3);
CompletionService<DownloadedItem> completionService = new ExecutorCompletionService<DownloadedItem>(executor);

while list has URL do {
   executor.submit(new DownloadJob(list.getNextURL());//submit to queue for download
}

new Thread() {
    public void run() {
        while there is URL left do {
            Future<DownloadedItem> downloadedItem = executor.take();//take the result as soon as it finish
            analyzeExecutor.submit(new AnalyzeJob(downloadedItem.get());
        }
    }
};    
//...and so on