You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何使用Java Executor框架通过线程实现数字分组合并求和?

Java Executor框架实现分组合并求和方案

嘿,这个分组合并求和的需求刚好能用上ExecutorService + Future的组合来实现递归式的并行计算,我帮你梳理清楚步骤,附上完整的可运行代码,你可以对照着自己的现有代码调整:

核心思路

我们的目标是:

  1. 将初始数字两两分组,每组用独立线程计算和
  2. 收集所有分组的计算结果后,再重复两两分组→线程计算的过程
  3. 直到最终只剩一个结果,就是所有数字的总和
    核心依赖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

火山引擎 最新活动