使用ExecutorService并行地重复执行许多类似的任务

时间:2015-02-20 15:12:01

标签: java multithreading concurrency executorservice

有简化的Java代码如下所示:

while(someCondition)
{
    SomeType a = CalcResult(param1);
    SomeType b = CalcResult(param2);
    SomeType c = CalcResult(param3);

    // Do something with a, b and c
}

CalcResult()非常耗时。该应用程序在SMP系统上运行。可以尝试在自己的CPU上同时运行所有三个计算,而不是按顺序运行它们。总是那3个任务需要并行,而不是任意数量的任务(例如算法)。每个任务可能比其他任务花费更多或更少的时间,但通常差异不是那么大(20-30%)。

当他们需要返回结果时,我查看了来自https://stackoverflow.com/a/9148992/2721750的Executor服务解决方案:

ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Integer> callable = new Callable<Integer>() {
    @Override
    public Integer call() {
        return 2;
    }
};
Future<Integer> future = executor.submit(callable);
// future.get() returns 2
executor.shutdown();

由于我在Java方面的经验主要是在servlet / JSP开发中,所以我没有使用线程的经验,也不确定该代码片段是否适用于3个任务而不是1个。

如何提交3个任务,每个任务都有自己的参数值,等待直到所有这些任务都返回计算结果,同时确保为他们创建线程不会否定自己运行的优势CPU ,即有没有办法在while()循环之前创建一次线程,然后简单地将一个新的paramN推入循环内的每个线程中,唤醒它们,并且等到他们完成所有计算?

3 个答案:

答案 0 :(得分:4)

Executors.newSingleThreadExecutor()将只创建一个线程。你想要的是Executors.newFixedThreadPool(3)。在while循环之前调用它,因此线程只创建一次。

创建一个Callable包装器:

class MyCallable implements Callable<V> {
    P p;
    MyCallable(P parameter) {
        p = parameter;
    }
    V call() {
        return CalcResult(p);
    }
}

while loop:

ExecutorService executor = Executors.newFixedThreadPool(3);
while (cond) {
    Future<V> aFuture = executor.submit(new MyCallable(param1));
    Future<V> bFuture = executor.submit(new MyCallable(param2));
    Future<V> cFuture = executor.submit(new MyCallable(param3));

    // this will block until all calculations are finished:
    a = aFuture.get();
    b = bFuture.get();
    c = cFuture.get();

   // do something with a/b/c, e.g. calc new params.
}

答案 1 :(得分:2)

  

我没有使用Java线程的经验

然后你在这里玩火。并发性很棘手,因为顺序编程无法实现故障模式,但运行时并不能避免您自己的错误。因此,在您从SO中粘贴代码之前,您应该至少阅读Java Concurrency in Practice的第1-6章,以了解正在发生的事情。

答案 2 :(得分:1)

您可以为应用程序创建执行程序服务,特别是用于处理这些计算。池大小可能会有所不同,具体取决于您是否要同时运行多个计算:

ExecutorService service = Executors.newFixedThreadPool(3);

Future<Integer> submitA = service.submit(new Callable<Integer>() {
  @Override
  public Integer call() throws Exception {
    return processA();
  }
});
Future<Integer> submitB = service.submit(new Callable<Integer>() {
  @Override
  public Integer call() throws Exception {
    return processB();
  }
});
Future<Integer> submitC = service.submit(new Callable<Integer>() {
  @Override
  public Integer call() throws Exception {
    return processC();
  }
});

int result = submitA.get() + submitB.get() + submitC.get();

在这种情况下,您需要确保每次运行计算时都不创建线程池,在这种情况下,与保存相比,创建池的影响会很小 - 假设运行时间将为通过分割任务来减少。