Java中如何并行执行两个代码块并获取结果完成后续处理
实现并行任务等待及结果合并的Java方案
嘿,这个场景太常见啦!要实现Block A和Block B并行执行、等两者都完成后再用结果处理Block C的逻辑,Java里有好几种靠谱的办法,我给你详细说说:
优先推荐:用CompletableFuture(Java 8及以上)
Java 8引入的CompletableFuture简直是为这种异步合并场景量身打造的,代码简洁还容易维护。直接看示例:
import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class TestService { Object someBigMethod(String A, Integer I) { // 异步启动Block A的数据库操作 CompletableFuture<Object> futureA = CompletableFuture.supplyAsync(() -> { // 这里写你的耗时DB读取逻辑,返回结果就行 return fetchDataForA(A); }); // 同时启动Block B的数据库操作,和A并行跑 CompletableFuture<Object> futureB = CompletableFuture.supplyAsync(() -> { // 另一项耗时DB读取逻辑 return fetchDataForB(I); }); // Block C:等A和B都跑完,拿结果构建目标对象 try { Object resultA = futureA.get(); Object resultB = futureB.get(); // 这里就是你合并结果、处理后续逻辑的地方 return assembleTargetObject(resultA, resultB); } catch (InterruptedException | ExecutionException e) { // 别忘处理异常!比如任务被中断、DB查询失败啥的 e.printStackTrace(); throw new RuntimeException("并行任务执行出错", e); } } // 模拟你的数据库读取方法A private Object fetchDataForA(String paramA) { // 实际换成JDBC、MyBatis或者JPA的查询逻辑 return "DB Result for A: " + paramA; } // 模拟你的数据库读取方法B private Object fetchDataForB(Integer paramI) { return "DB Result for B: " + paramI; } // 模拟结果合并构建目标对象的方法 private Object assembleTargetObject(Object aResult, Object bResult) { // 实际逻辑:把两个结果整合成你需要的对象 return String.format("%s | %s", aResult, bResult); } }
要是想让代码更链式更优雅,还可以用thenCombine直接把合并逻辑串起来:
CompletableFuture<Object> combinedResult = futureA.thenCombine(futureB, (a, b) -> assembleTargetObject(a, b)); try { return combinedResult.get(); } catch (InterruptedException | ExecutionException e) { // 异常处理同上 }
默认情况下supplyAsync会用JDK的ForkJoinPool.commonPool,如果需要自定义线程池(比如控制并发数、和DB连接池匹配),直接传个自定义Executor进去就行:
// 比如创建一个固定大小的线程池 Executor customPool = Executors.newFixedThreadPool(3); CompletableFuture<Object> futureA = CompletableFuture.supplyAsync(() -> fetchDataForA(A), customPool);
兼容老版本:ExecutorService + Future(Java 5及以上)
如果你的项目还在Java 8以下,用ExecutorService也能搞定,就是代码稍微啰嗦一点:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ExecutionException; public class TestService { // 可以把线程池做成全局的,复用线程避免频繁创建销毁 private static final ExecutorService taskPool = Executors.newFixedThreadPool(2); Object someBigMethod(String A, Integer I) { // 提交A任务到线程池 Future<Object> futureA = taskPool.submit(() -> fetchDataForA(A)); // 提交B任务,和A并行执行 Future<Object> futureB = taskPool.submit(() -> fetchDataForB(I)); try { // 等两个任务都完成,取结果 Object resultA = futureA.get(); Object resultB = futureB.get(); return assembleTargetObject(resultA, resultB); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); throw new RuntimeException("并行任务执行失败", e); } // 注意:如果是全局线程池,这里别shutdown,否则下次调用就用不了了 // 要是每次方法调用都新建线程池,记得在finally里shutdown } // 下面的fetchDataForA、fetchDataForB、assembleTargetObject和上面一样 private Object fetchDataForA(String paramA) { return "DB Result for A: " + paramA; } private Object fetchDataForB(Integer paramI) { return "DB Result for B: " + paramI; } private Object assembleTargetObject(Object aResult, Object bResult) { return String.format("%s | %s", aResult, bResult); } }
几个要注意的点
- 异常处理不能少:不管用哪种方案,只要调用
get()就可能抛出中断异常或者任务执行异常,一定要捕获处理,别让程序直接崩了。 - 线程池要适配:别随便用默认线程池!尤其是高并发场景,最好根据你的数据库连接池大小来设置线程池的并发数,避免同时发起太多DB连接导致性能下降。
- DB连接要够:并行执行的前提是数据库连接池有足够的空闲连接,不然任务会卡在获取连接这一步,反而变慢。
内容的提问来源于stack exchange,提问作者Michael Sampson
相关产品推荐
相关产品推荐

