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

aje*_*jeh 3 java concurrency multithreading executorservice

Java 代码简化后如下所示:

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

    // Do something with a, b and c
}
Run Code Online (Sandbox Code Playgroud)

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

由于他们需要返回结果,我从/sf/answers/640429471/查看了执行器服务解决方案:

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();
Run Code Online (Sandbox Code Playgroud)

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

如何提交3个任务,每个任务都有自己的参数值,并等待所有任务都返回计算结果,同时确保为它们创建线程不会抵消在自己的CPU上运行的优势,即是否有一个在循环开始之前创建线程一次while(),然后简单地将新的paramN线程推入循环内的每个线程,唤醒它们,并等待它们执行所有计算的方法?

Adr*_*ard 5

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

创建一个可调用包装器:

class MyCallable implements Callable<V> {
    P p;
    MyCallable(P parameter) {
        p = parameter;
    }
    V call() {
        return CalcResult(p);
    }
}
Run Code Online (Sandbox Code Playgroud)

while 循环:

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.
}
Run Code Online (Sandbox Code Playgroud)

  • @ajeh 请参阅第二个片段...每次调用“get”都会阻塞,直到相应的 future 完成其任务。因此,当“cFuture.get()”返回时,所有三个任务都已完成。 (3认同)
  • @ajeh 将 `class MyCallable&lt;Double, Integer&gt;` 更改为 `class MyCallable` 并将类定义移动到单独的文件中。 (2认同)