我正在寻找一种在java中执行批量任务的方法。我们的想法是让一个基于线程池的ExecutorService
允许我在Callable
线程的不同线程中传播一组main
。此类应提供 waitForCompletion 方法,该方法将main
线程置于休眠状态,直到执行所有任务。然后应该唤醒main
线程,它将执行一些操作并重新提交一组任务。
此过程将重复多次,因此我想使用ExecutorService.shutdown
,因为这需要创建ExecutorService
的多个实例。
目前,我已使用AtomicInteger
和Lock
/ 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
似乎不是一种选择。
这是一种有效的方法吗?是否有更有效/更优雅的方式来实现它?
由于
答案 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