如何使用Java Executor框架通过线程实现数字分组合并求和?
Java Executor框架实现分组合并求和方案
嘿,这个分组合并求和的需求刚好能用上ExecutorService + Future的组合来实现递归式的并行计算,我帮你梳理清楚步骤,附上完整的可运行代码,你可以对照着自己的现有代码调整:
核心思路
我们的目标是:
- 将初始数字两两分组,每组用独立线程计算和
- 收集所有分组的计算结果后,再重复两两分组→线程计算的过程
- 直到最终只剩一个结果,就是所有数字的总和
核心依赖ExecutorService来管理线程池,用Future来获取异步任务的计算结果,通过递归实现多轮合并。
完整代码实现
1. 定义Callable任务类
因为需要线程返回计算结果,所以用Callable<Integer>而不是Runnable:
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; import java.util.stream.Collectors; import java.util.stream.IntStream; class SumTask implements Callable<Integer> { private final int a; private final int b; // 构造方法传入要相加的两个数 public SumTask(int a, int b) { this.a = a; this.b = b; } @Override public Integer call() throws Exception { // 这里可以模拟耗时操作,方便观察线程并行执行的过程 Thread.sleep(100); int sum = a + b; System.out.printf("线程%s完成计算:%d + %d = %d%n", Thread.currentThread().getName(), a, b, sum); return sum; } }
2. 递归合并计算方法
这个方法是核心,负责分组、提交任务、收集结果、递归合并:
public class MergeSumExecutor { private static int mergeSum(ExecutorService executor, List<Integer> numbers) throws Exception { // 终止条件:只剩一个数字,直接返回 if (numbers.size() == 1) { return numbers.get(0); } List<Future<Integer>> futures = new ArrayList<>(); // 两两分组处理,兼容奇数个数字的情况 for (int i = 0; i < numbers.size(); i += 2) { if (i + 1 < numbers.size()) { // 两个数一组,提交求和任务到线程池 futures.add(executor.submit(new SumTask(numbers.get(i), numbers.get(i + 1)))); } else { // 奇数个数字时,最后一个数直接作为结果,无需计算 futures.add(CompletableFuture.completedFuture(numbers.get(i))); } } // 收集所有异步任务的结果,形成新的中间结果列表 List<Integer> intermediateResults = new ArrayList<>(); for (Future<Integer> future : futures) { // future.get()会阻塞当前线程,直到任务完成并返回结果 intermediateResults.add(future.get()); } // 递归处理中间结果列表,继续合并 return mergeSum(executor, intermediateResults); } public static void main(String[] args) { // 初始化10个数字(示例用1到10) List<Integer> numbers = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList()); System.out.println("初始数字列表:" + numbers); // 创建固定大小的线程池,大小可以根据实际需求调整(比如CPU核心数、分组数) ExecutorService executor = Executors.newFixedThreadPool(5); try { int finalSum = mergeSum(executor, numbers); System.out.println("=================================="); System.out.println("最终求和结果:" + finalSum); } catch (Exception e) { e.printStackTrace(); } finally { // 正确关闭线程池,避免资源泄漏 executor.shutdown(); try { // 等待线程池中的任务完成,最多等60秒 if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { // 超时后强制关闭 executor.shutdownNow(); } } catch (InterruptedException e) { executor.shutdownNow(); } } } }
关键细节说明
- 线程池选择:这里用
newFixedThreadPool,线程数量固定,适合任务数量可控的场景;如果你的分组数量波动大,也可以用newCachedThreadPool自动扩容,但要注意资源消耗。 - Future.get()的阻塞特性:每一轮合并都需要等待所有分组任务完成,才能进行下一轮,这是符合你需求的——必须拿到上一轮的所有结果才能重新分组。
- 奇数个数字处理:当列表长度为奇数时,最后一个数字不需要计算,直接加入中间结果,避免索引越界。
- 线程池关闭:必须在finally块中关闭线程池,用
shutdown()和awaitTermination()确保所有任务完成后再释放资源,避免程序挂起。
运行效果示例
你运行代码后会看到类似这样的输出(线程名称可能不同):
初始数字列表:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 线程pool-1-thread-1完成计算:1 + 2 = 3 线程pool-1-thread-2完成计算:3 + 4 = 7 线程pool-1-thread-3完成计算:5 + 6 = 11 线程pool-1-thread-4完成计算:7 + 8 = 15 线程pool-1-thread-5完成计算:9 + 10 = 19 线程pool-1-thread-1完成计算:3 + 7 = 10 线程pool-1-thread-2完成计算:11 + 15 = 26 线程pool-1-thread-3完成计算:19 = 19 线程pool-1-thread-1完成计算:10 + 26 = 36 线程pool-1-thread-2完成计算:36 + 19 = 55 ================================== 最终求和结果:55
内容的提问来源于stack exchange,提问作者Eshu




