Java - 将工作分割为多个线程

Bob*_*r02 11 java multithreading executorservice

我提出了以下问题:出于性能原因,我需要跨多个线程拆分工作,但我不确定采取什么方法.

首先,我将提供的任务应该返回一个值并获取一个参数.另外,主要方法(做主要工作,而不是static main())已经在单独的线程上运行,并定期调用.此外,此方法必须在某个时刻等待所有线程完成然后继续.

一种方法(对我来说最明显)是将每个作业安排在一个单独的线程上并将结果存储在类变量中:

public Object result1, result2;

public void mainMethod() throws InterruptedException {
    final Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            result1 = expensiveMethod("param1");
        }
    });

    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            result2 = expensiveMethod("param2");
        }
    });

    thread1.join();
    thread.join();

    //Do rest of work
}

private Object expensiveMethod(Object param){
    // Do work and return result
}
Run Code Online (Sandbox Code Playgroud)

这有点难看并且不太理想,因为正如我所说,mainMethod被多次调用,我不希望在设置结果变量时有任何竞争条件.理想情况下,我想让它们成为局部变量,但我不能在run方法中访问它们,除非它们是final,然后我不能为它们赋值......

另一种方法,我虽然这样做是这样的:

public void mainMethod() throws InterruptedException, ExecutionException {
    String obj1, obj2;

    final ExecutorService executorService = Executors.newFixedThreadPool(16);
    final Future<String> res1 = executorService.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return expensiveMethod("param1");
        }
    });
    final Future<String> res2 = executorService.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return expensiveMethod("param2");
        }
    });

    obj1 = res1.get();
    obj2 = res2.get();

}

private String expensiveMethod(String param) {
    // Do work and return result
}
Run Code Online (Sandbox Code Playgroud)

这会自动从main方法等待这两个计算,并允许我在本地存储结果.你觉得怎么样?还有其他方法吗?

Tom*_*icz 13

您的方法ExecutorService几乎是最现代和最安全的方法.建议将Callables 提取到单独的类:

public class ExpensiveTask implements Callable<String> {

    private final String param;

    public ExpensiveTask(String param) {
        this.param = param;
    }

    @Override
    public String call() throws Exception {
        return expensiveMethod(param);
    }

}
Run Code Online (Sandbox Code Playgroud)

这将使您的代码更清洁:

final ExecutorService executorService = Executors.newFixedThreadPool(16);
final Future<String> res1 = executorService.submit(new ExpensiveTask("param1"));
final Future<String> res2 = executorService.submit(new ExpensiveTask("param2"));
String obj1 = res1.get();
String obj2 = res2.get();
Run Code Online (Sandbox Code Playgroud)

几点说明:

  • 如果您只想同时处理两个任务,或者您想要从多个客户端线程重用该池,那么16个线程太多了?

  • 记得关闭游泳池

  • 使用轻量级ExecutorCompletionService等待完成的第一个任务,不一定是第一个提交的任务.

如果您需要完全不同的设计理念,请查看及其基于actor的并发模型.