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

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

首先,我将提供的任务应该返回一个值并获取一个参数。 此外,主要方法(执行主要工作,而不是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 } 

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

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

 public void mainMethod() throws InterruptedException, ExecutionException { String obj1, obj2; final ExecutorService executorService = Executors.newFixedThreadPool(16); final Future res1 = executorService.submit(new Callable() { @Override public String call() throws Exception { return expensiveMethod("param1"); } }); final Future res2 = executorService.submit(new Callable() { @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 } 

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

使用ExecutorService方法几乎是最现代和最安全的方法。 建议将Callable提取为单独的类:

 public class ExpensiveTask implements Callable { private final String param; public ExpensiveTask(String param) { this.param = param; } @Override public String call() throws Exception { return expensiveMethod(param); } } 

这将使您的代码更清洁:

 final ExecutorService executorService = Executors.newFixedThreadPool(16); final Future res1 = executorService.submit(new ExpensiveTask("param1")); final Future res2 = executorService.submit(new ExpensiveTask("param2")); String obj1 = res1.get(); String obj2 = res2.get(); 

几点说明:

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

  • 记得关闭游泳池

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

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

您可以使用ExecutorService

 ExecutorService pool = Executors.newFixedThreadPool(4); List> futures = new ArrayList>(10); for(int i = 0; i < 10; i++){ futures.add(pool.submit(new StringTask())); } for(Future future : futures){ String result = future.get(); //Compute the result } pool.shutdown(); 

或者使用CompletionService

 ExecutorService threadPool = Executors.newFixedThreadPool(4); CompletionService pool = new ExecutorCompletionService(threadPool); for(int i = 0; i < 10; i++){ pool.submit(new StringTask()); } for(int i = 0; i < 10; i++){ String result = pool.take().get(); //Compute the result } threadPool.shutdown(); 

检查这个以供参考

首先,您可能希望从mainMethod()外部化ExecutorService的创建如果经常调用它,则可能会创建大量线程。

Future方法更好,因为这正是Futures的用途。 此外,它使阅读代码更容易。

更轻松的说明,尽管您可能必须将对象定义为final,但无论您的引用是否为final,都可以在对象上设置setter方法,可能允许您更改最终对象的值。 (参考文献不是最终对象!)

我将在我眼中添加一个比为参数化Callable创建一个全新类更优雅的提议。 我的解决方案是一个返回Callable实例的方法:

 Callable expensive(final String param) { return new Callable() { public String call() { return expensiveMethod(param); }}; } 

这甚至使客户端代码更加可口:

 final Future f1 = executor.submit(expensive("param1")); 

略有不同的方法是:

  • 创建一个LinkedBlockingQueue

  • 将它传递给每个任务。 任务可以是jucExecutor上的Threads或Runnables。

  • 每个任务都将其结果添加到队列中

  • 主线程在循环中使用queue.take()读取结果

这样,结果一经计算就会得到处理。

您希望使用CompletionService并跟踪提交的任务。
在循环中,然后在完成所有任务后,取出()并退出循环。
比例非常好,您以后可以添加更多任务。

 private static final ExecutorService threadpool = Executors.newFixedThreadPool(3); ArrayList>> futures = new ArrayList>>(); for (ArrayList data : map.values()) { final Future> future = threadpool.submit(new ValidationTaskExecutor(data)); futures.add(future); } List> res = new ArrayList>(); for (Future> future : futures) { allValidRequest.addAll(future.get()); }