Java任务队列,线程池和带回调的线程,以便在新任务可以启动时通知

时间:2012-02-24 11:58:52

标签: java multithreading callback threadpool

我试图找到一种方法,如何在具有一定最大线程限制的多个线程中加载多个网页,以便在完成一个新页面时加载。在下载页面后,还应该有加载内容的另一个后处理线程,以便整个过程被链接。

我想怎么做:

  • 任务队列保存应下载的页面
  • Threadpool有一定数量的线程在任务队列中下载页面(加载页面需要一些时间,所以数量为 线程可以远高于cpu核心的数量)
  • 当页面下载完成后,线程应该通知这个,以便可以启动队列中的新任务
  • 页面下载完成后,内容应转移到另一个任务队列进行后期处理

  • 另一个线程池拥有尽可能多的线程,因为cpu具有内核(猜测每个内核只有一个线程用于后处理是最快的),此线程池对下载的页面执行后处理。

  • 当页面的后处理完成后,线程应该通知它,以便队列中的另一个页面可以进行后处理

  • 当所有页面都已下载(队列为空)时,可以关闭第一个线程池,当两个任务队列都为空(所有页面都已下载并进行后处理)时,可以关闭另一个线程池< / p>

我有类似的东西:

            for (int j = 0; j < threads.length; j++) {
            threads[j].start();
        }

        for (int j = 0; j < threads.length; j++) {
            threads[j].join();
        }

但是这样加载的所有页面都在同一个单独的线程中,我想限制线程数。更重要的是,我希望重用线程并使线程在一个任务完成时执行下一个任务。我可以用while循环来做到这一点,但这是我想要避免的,我不想要一个while循环来检查队列是否有更多任务以及线程是否空闲。是否可以使用某种回调,以便线程告诉已完成的池并返回数据。 我还希望下载任务能够将内容存储在数据结构中,并将其添加到后处理任务队列中。

我到目前为止找到的最佳资源是: Thread pools Callback

但我不知道是否有可能按照我想要的方式创建它。我一直在考虑函数指针。

3 个答案:

答案 0 :(得分:3)

不要使用低级线程方法来执行此操作。拥有downloadExecutor个线程池,并向此池提交DownloadTask个实例(实现RunnableCallable)。

在DownloadTask代码的末尾,将一个PostProcessPageTask实例(再次实现实现RunnableCallable)提交给第二个postProcessExecutor线程池。

您可以使用一个或两个CountDownLatch实例,每个任务在完成时都会减少,并让主线程在此(或这些)锁存器上等待,以了解何时必须关闭线程池。

有关详细信息,请参阅http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html和docs.oracle.com/javase/6/docs/api/java/util/concurrent/CountDownLatch.html。

答案 1 :(得分:1)

您可以使用Guava的ListenableFutures

首先,您需要向ListenableExecutorService提交下载任务,然后通过Futures.transform使用后期处理器转换生成的期货。

ListenableExecutorService dlPool = MoreExecutors.listeningDecorator(firstPool);
ListenableExecutorService procPool = MoreExecutors.listeningDecorator(secondPool);

List<ListenableFuture<Result>> results = new ArrayList<...>();
for (String url : urls) {
  // download task
  ListenableFuture<String> html = dlPool.submit(...);
  // post process
  ListenableFuture<Result> result = Futures.transform(html,
    new Function<String, Result>() {
      ... // post process
    }, procPool);
  results.add(result);
}

// blocks until all results are processed
List<Result> processed = Futures.allAsList(results).get();

firstPool.shutdownNow();
secondPool.shutdownNow();

答案 2 :(得分:-1)

不要试图手动编写这种类型的通用基础设施。

Java 5及更高版本附带了可爱的java.util.concurrent包

这应该是您制作多线程应用程序时首先要做的事情。

它有很多通用工具,比如线程池(执行Runnable或Callable对象),并会为你做很多狗工作。

互联网上有大量免费资源,或者如果您更喜欢书籍,Brian Goetz的“实践中的Java并发”被广泛认为是最好的之一。