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()); }
- 如何在Tomcat停止时停止执行程序线程?
- 完成所有ExecutorService任务后,程序不会立即终止
- ExecutorService应该是静态的还是全局的
- Java支持三种不同的并发模型
- 如何在multithreading环境中更好地使用ExecutorService?
- 在ThreadPoolExecutor中使用InheritableThreadLocal – 或者 – 不重用线程的ThreadPoolExecutor
- 无法停止使用ExecutorService启动的任务
- 使用ExecutorService执行异步任务时出现问题
- 如何在使用具有线程超时function的ExecutorService时提高性能?