如何实现ExecutorService来执行批量任务

时间:2012-04-24 12:46:12

标签: java multithreading executorservice

我正在寻找一种在java中执行批量任务的方法。我们的想法是让一个基于线程池的ExecutorService允许我在Callable线程的不同线程中传播一组main。此类应提供 waitForCompletion 方法,该方法将main线程置于休眠状态,直到执行所有任务。然后应该唤醒main线程,它将执行一些操作并重新提交一组任务。

此过程将重复多次,因此我想使用ExecutorService.shutdown,因为这需要创建ExecutorService的多个实例。

目前,我已使用AtomicIntegerLock / Condition

按以下方式实施
public class BatchThreadPoolExecutor extends ThreadPoolExecutor {
  private final AtomicInteger mActiveCount;
  private final Lock          mLock;
  private final Condition     mCondition;

  public <C extends Callable<V>, V> Map<C, Future<V>> submitBatch(Collection<C> batch){
    ...
    for(C task : batch){
      submit(task);
      mActiveCount.incrementAndGet();
    }
  }

  @Override
  protected void afterExecute(Runnable r, Throwable t) {
    super.afterExecute(r, t);
    mLock.lock();
    if (mActiveCount.decrementAndGet() == 0) {
      mCondition.signalAll();
    }
    mLock.unlock();
  }

  public void awaitBatchCompletion() throws InterruptedException {
    ...
    // Lock and wait until there is no active task
    mLock.lock();
    while (mActiveCount.get() > 0) {
      try {
        mCondition.await();
      } catch (InterruptedException e) {
        mLock.unlock();
        throw e;
      }
    }
    mLock.unlock();
  } 
}

请注意,我不一定会立即从批处理中提交所有任务,因此CountDownLatch似乎不是一种选择。

这是一种有效的方法吗?是否有更有效/更优雅的方式来实现它?

由于

3 个答案:

答案 0 :(得分:8)

我认为ExecutorService本身将能够满足您的要求。

致电invokeAll([...])并迭代您的所有任务。如果您可以遍历所有期货,那么所有任务都已完成。

答案 1 :(得分:3)

正如其他答案所指出的那样,您的用例似乎没有任何需要自定义ExecutorService的部分。

在我看来,您需要做的就是提交批处理,等待它们全部完成而忽略主线程上的中断,然后根据第一批的结果提交另一批。我认为这只是一个问题:

    ExecutorService service = ...;

    Collection<Future> futures = new HashSet<Future>();
    for (Callable callable : tasks) {
        Future future = service.submit(callable);
        futures.add(future);
    }

    for(Future future : futures) {
        try {
            future.get();
        } catch (InterruptedException e) {
            // Figure out if the interruption means we should stop.
        }
    }

    // Use the results of futures to figure out a new batch of tasks.
    // Repeat the process with the same ExecutorService.

答案 2 :(得分:0)

我同意@ckuetbach的默认Java Executors应该为您提供执行“批量”工作所需的所有功能。

如果我是你,我会提交一堆工作,等待他们完成ExecutorService.awaitTermination(),然后启动一个新的ExecutorService。这样做是为了节省“线程创建”,这是不成熟的优化,除非你每秒做100次这样的事情。

如果您确实坚持为每个批次使用相同的ExecutorService,那么您可以自己分配ThreadPoolExecutor,并在循环中查看ThreadPoolExecutor.getActiveCount()。类似的东西:

BlockingQueue jobQueue = new LinkedBlockingQueue<Runnable>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(NUM_THREADS, NUM_THREADS,
    0L, TimeUnit.MILLISECONDS, jobQueue);
// submit your batch of jobs ...
// need to wait a bit for the jobs to start
Thread.sleep(100);
while (executor.getActiveCount() > 0 && jobQueue.size() > 0) {
    // to slow the spin
    Thread.sleep(1000);
}
// continue on to submit the next batch