多线程Future处理list数据

/**
     * 多线程处理list数据
     */
    public Map<String, Object> execuleThread() {
        Map<String, Object> ret = new LinkedHashMap<>();
        try {
            Long t1 = System.currentTimeMillis();
            List<BehospitalInfo> behospitalInfoList = new ArrayList<>(); // 需要处理的列表数据
            int threadNum = 4; // 推荐的总线程数 = cpu个数 * 每个cpu的核数
            List<List<BehospitalInfo>> threadData = divideByCopies(behospitalInfoList, threadNum);
            List<Future<String>> list = new ArrayList<>();

            ExecutorService executor = Executors.newFixedThreadPool(threadNum);
            for (int i = 0; i < threadNum; i++) {
                final int index = i; // 这一行代码很重要,如果使用成员变量,下面会报错
                Future<String> future = executor.submit(new Callable<String>() {
                    @Override
                    public String call() {
                        StringBuffer sb = new StringBuffer();
                        int sucNum = 0, errNum = 0;
                        List<BehospitalInfo> data = threadData.get(index);
                        System.out.println("******************************线程" + index + "正在执行");
                        if (ListUtil.isNotEmpty(data)) {
                            for (BehospitalInfo bean : data) {
                                // 处理业务数据
                                if (处理成功) {
                                    sucNum++;
                                } else {
                                    errNum++;
                                }
                            }
                            sb.append("线程" + index + "执行完毕:共【" + data.size() + "】条,成功【" + sucNum + "】条,失败【" + errNum + "】条。");
                        } else {
                            sb.append("线程" + index + "未执行数据");
                        }
                        return sb.toString();
                    }
                });
                list.add(future);
            }

            // 拼接返回数据
            for (int i = 0; i < list.size(); i++) {
                Future<String> future = list.get(i);
                while(!future.isDone()); // 这一行代码很重要
                ret.put("线程" + i , future.get());
            }
            Long t2 = System.currentTimeMillis();
            ret.put("总条数", behospitalInfoList.size());
            ret.put("共执行时间:", (t2 - t1) / 1000 + "秒");
        } catch (Exception e) {
            throw new CommonException(CommonErrorCode.SERVER_IS_ERROR);
        }
        return  ret;
    }

    /**
     * 将列表按列表总长度划分
     *
     * @param originList 数据
     * @param num 份数
     * @param <T>
     * @return
     */
    public <T> List<List<T>> divideByCopies(List<T> originList, int num) {
        List<List<T>> list = new ArrayList<>();
        if (ListUtil.isEmpty(originList) || num < 0) {
            return null;
        }
        for (int i = 0; i < num; i++) {
            list.add(new ArrayList<T>());
        }
        for (int i = 0; i < originList.size(); i++) {
            list.get(i % num).add(originList.get(i));
        }

        return list;
    }
<think>好的,我需要帮助用户解决如何修改Java ExecutorService中的Future类型,使其包含String和Boolean,并通过getName获取文件名,同时实现失败重试机制。首先,我需要理解用户的具体需求。用户希望每个Future任务不仅返回一个布尔值表示下载是否成功,还要保存文件名archStore.getName。同时,如果下载失败,需要重试。 首先,确定如何让Future保存这两个信息。通常,Future的泛型类型可以是自定义的对象,包含文件名和状态。所以,我应该建议用户创建一个DownloadResult类,包含fileName和success两个字段。这样,每次任务执行后返回这个对象,Future的类型就是DownloadResult。 接下来,用户提到要通过getName获取文件名。在Callable任务中,应该在任务执行时获取archStore.getName(),并将其包含在返回的结果对象中。因此,在实现Callable的时候,需要确保能访问到archStore对象,并在call()方法中捕获文件名和下载结果。 然后是失败重试机制。用户可能需要记录哪些任务失败了,然后重新提交它们。可以考虑在任务执行时捕获异常,如果失败,就将任务加入重试队列,或者直接进行重试。不过,直接在Callable任务中重试可能会阻塞线程池,更好的方法可能是在获取Future结果后检查是否成功,若失败则重新提交任务。但需要注意重试次数限制,避免无限循环。 另外,线程池的管理也很重要。使用ExecutorService提交任务后,需要正确关闭线程池,并在所有任务完成后处理重试。可能需要收集所有的Future对象,检查每个的结果,如果success为false,则重新创建任务并提交。 还需要考虑线程安全的问题,比如使用并发集合来存储需要重试的任务,或者在重试时避免竞态条件。例如,可以使用CopyOnWriteArrayList来存储失败的任务,然后重新提交。 代码结构方面,用户需要修改Callable的返回类型为DownloadResult,在call方法中执行下载逻辑,捕获异常,返回包含文件名和成功状态的结果。主线程在获取所有Future后,遍历结果,收集失败的任务,重新提交给ExecutorService进行重试。 可能还需要调整线程池的大小,确保有足够的线程处理重试任务,或者使用单独的线程池来处理重试,避免影响主任务。 总结步骤: 1. 创建DownloadResult类封装文件名和状态。 2. 修改Callable任务返回DownloadResult,并在call方法中获取文件名。 3. 提交任务并收集Future对象。 4. 检查每个Future的结果,记录失败的任务。 5. 对失败的任务重新提交,实现重试机制。 6. 确保线程池正确关闭,处理所有重试任务。 需要注意异常处理,尤其是在重试时可能需要限制重试次数,避免无限重试。另外,在获取Future的get()方法时,要处理可能的异常,比如ExecutionException,以确定任务失败的原因。 参考引用中的Callable使用方式,用户已经知道如何提交任务和获取结果。现在需要扩展结果类型,并在结果处理阶段加入重试逻辑。引用[3]提到ExecutorService的基本步骤,可以用来指导任务提交和关闭。引用[4]中的并发集合可能用于安全地存储失败任务,以便重试。</think>### 分步解决方案 #### 1. **定义包含文件名和状态的返回值类型** 创建一个`DownloadResult`类,封装文件名和下载结果: ```java public class DownloadResult { private String fileName; private Boolean success; public DownloadResult(String fileName, Boolean success) { this.fileName = fileName; this.success = success; } // Getters public String getFileName() { return fileName; } public Boolean getSuccess() { return success; } } ``` #### 2. **修改Callable任务逻辑** 在任务中获取文件名`archStore.getName()`,并返回组合结果: ```java Callable<DownloadResult> task = () -> { String fileName = archStore.getName(); // 假设archStore是外部变量 try { // 执行下载逻辑 boolean downloadSuccess = downloadFile(fileName); return new DownloadResult(fileName, downloadSuccess); } catch (Exception e) { return new DownloadResult(fileName, false); // 标记为失败 } }; ``` #### 3. **提交任务并记录Future对象** 通过`ExecutorService`提交任务,并保存`Future`对象用于后续检查: ```java ExecutorService executor = Executors.newFixedThreadPool(4); List<Future<DownloadResult>> futures = new ArrayList<>(); // 提交初始任务 for (ArchStore store : stores) { Future<DownloadResult> future = executor.submit(createTask(store)); // 封装为方法 futures.add(future); } ``` #### 4. **实现重试机制** 检查结果并重新提交失败任务(最多重试3次): ```java int maxRetries = 3; Map<String, Integer> retryCountMap = new ConcurrentHashMap<>(); // 记录重试次数[^4] // 处理结果并重试 for (int i = 0; i < maxRetries; i++) { List<Future<DownloadResult>> retryFutures = new ArrayList<>(); for (Future<DownloadResult> future : futures) { try { DownloadResult result = future.get(); if (!result.getSuccess()) { String fileName = result.getFileName(); int count = retryCountMap.getOrDefault(fileName, 0); if (count < maxRetries) { retryCountMap.put(fileName, count + 1); retryFutures.add(executor.submit(createTask(fileName))); // 重新提交 } } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } futures = retryFutures; // 更新待处理列表 } ``` #### 5. **关闭线程池** 在所有任务完成后关闭线程池: ```java executor.shutdown(); try { executor.awaitTermination(1, TimeUnit.HOURS); } catch (InterruptedException e) { executor.shutdownNow(); } ``` ### 关键点说明 - **返回值扩展**:通过自定义对象`DownloadResult`,将文件名和结果绑定,避免状态丢失[^1]。 - **线程安全**:使用`ConcurrentHashMap`记录重试次数,保证多线程环境下的安全性。 - **重试控制**:限制最大重试次数,防止资源耗尽。 - **异常处理**:在`Callable`中捕获异常并标记失败,避免任务崩溃影响线程池。 ### 示例代码整合 ```java // 完整示例 public class DownloadManager { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(4); List<ArchStore> stores = getStores(); // 假设获取待下载列表 List<Future<DownloadResult>> futures = new ArrayList<>(); Map<String, Integer> retryMap = new ConcurrentHashMap<>(); // 提交初始任务 for (ArchStore store : stores) { futures.add(executor.submit(new DownloadTask(store))); } // 重试逻辑 for (int retry = 0; retry < 3; retry++) { List<Future<DownloadResult>> retryList = new ArrayList<>(); for (Future<DownloadResult> future : futures) { try { DownloadResult result = future.get(); if (!result.getSuccess()) { String fileName = result.getFileName(); int count = retryMap.getOrDefault(fileName, 0); if (count < 2) { // 允许最多3次(初始+2次重试) retryMap.put(fileName, count + 1); retryList.add(executor.submit(new DownloadTask(fileName))); } } } catch (Exception e) { e.printStackTrace(); } } futures = retryList; } executor.shutdown(); } static class DownloadTask implements Callable<DownloadResult> { private String fileName; public DownloadTask(ArchStore store) { this.fileName = store.getName(); } public DownloadTask(String fileName) { this.fileName = fileName; } @Override public DownloadResult call() { try { boolean success = downloadFile(fileName); return new DownloadResult(fileName, success); } catch (Exception e) { return new DownloadResult(fileName, false); } } private boolean downloadFile(String name) { // 实际下载逻辑 return Math.random() > 0.2; // 模拟80%成功率 } } } ``` ### 引用说明 - 线程池管理参考了`ExecutorService`的标准用法[^3]。 - 并发集合`ConcurrentHashMap`用于安全记录重试次数。 - `Callable`返回值扩展遵循了Java多线程编程规范。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

屁仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值