在Java中并行化任务的最简单方法是什么?

Edu*_*rdo 40 java parallel-processing multithreading

说我有一个类似的任务:

for(Object object: objects) {
    Result result = compute(object);
    list.add(result);
}
Run Code Online (Sandbox Code Playgroud)

并行化每个compute()的最简单方法是什么(假设它们已经可并行化)?

我不需要一个严格符合上述代码的答案,只需一般答案.但是如果您需要更多信息:我的任务是IO绑定的,这是针对Spring Web应用程序的,任务将在HTTP请求中执行.

ove*_*ink 60

我建议看看ExecutorService.

特别是这样的事情:

ExecutorService EXEC = Executors.newCachedThreadPool();
List<Callable<Result>> tasks = new ArrayList<Callable<Result>>();
for (final Object object: objects) {
    Callable<Result> c = new Callable<Result>() {
        @Override
        public Result call() throws Exception {
            return compute(object);
        }
    };
    tasks.add(c);
}
List<Future<Result>> results = EXEC.invokeAll(tasks);
Run Code Online (Sandbox Code Playgroud)

请注意,newCachedThreadPool如果objects是一个大清单,使用可能会很糟糕.缓存的线程池可以为每个任务创建一个线程!你可能想要使用newFixedThreadPool(n)n是合理的东西(比如你拥有的核心数,假设compute()是CPU绑定的).

这是实际运行的完整代码:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorServiceExample {
    private static final Random PRNG = new Random();

    private static class Result {
        private final int wait;
        public Result(int code) {
            this.wait = code;
        }
    }

    public static Result compute(Object obj) throws InterruptedException {
        int wait = PRNG.nextInt(3000);
        Thread.sleep(wait);
        return new Result(wait);
    }

    public static void main(String[] args) throws InterruptedException,
        ExecutionException {
        List<Object> objects = new ArrayList<Object>();
        for (int i = 0; i < 100; i++) {
            objects.add(new Object());
        }

        List<Callable<Result>> tasks = new ArrayList<Callable<Result>>();
        for (final Object object : objects) {
            Callable<Result> c = new Callable<Result>() {
                @Override
                public Result call() throws Exception {
                    return compute(object);
                }
            };
            tasks.add(c);
        }

        ExecutorService exec = Executors.newCachedThreadPool();
        // some other exectuors you could try to see the different behaviours
        // ExecutorService exec = Executors.newFixedThreadPool(3);
        // ExecutorService exec = Executors.newSingleThreadExecutor();
        try {
            long start = System.currentTimeMillis();
            List<Future<Result>> results = exec.invokeAll(tasks);
            int sum = 0;
            for (Future<Result> fr : results) {
                sum += fr.get().wait;
                System.out.println(String.format("Task waited %d ms",
                    fr.get().wait));
            }
            long elapsed = System.currentTimeMillis() - start;
            System.out.println(String.format("Elapsed time: %d ms", elapsed));
            System.out.println(String.format("... but compute tasks waited for total of %d ms; speed-up of %.2fx", sum, sum / (elapsed * 1d)));
        } finally {
            exec.shutdown();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 还要看看执行器,它充当各种执行器服务的工厂。 (2认同)

i00*_*174 7

使用 Java8 及更高版本,您可以在集合上使用parallelStream来实现此目的:

List<T> objects = ...;

List<Result> result = objects.parallelStream().map(object -> {
            return compute(object);
        }).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

注意:结果列表的顺序可能与对象列表中的顺序不一致。

在这个 stackoverflow 问题how-many-threads-are-spawned-in-parallelstream-in-java-8中提供了如何设置正确线程数的详细信息

  • 流是为数据并行而设计的,而不是为任务并行而设计的。请参阅 /sf/answers/1635955961/。 (3认同)

fas*_*ava 6

我们可以简单地创建几个线程并获得结果。

Thread t = new Mythread(object);

if (t.done()) {
   // get result
   // add result
}
Run Code Online (Sandbox Code Playgroud)

编辑:我认为其他解决方案更酷。