java使用CountDownLatch做并行查询

CountDownLatch 可以让多个查询任务并行执行,并在所有任务完成后统一汇总结果,从而缩短接口的整体响应时间。
代码中最重要的三个细节
  1. countDown() 必须放在 finally 中。

如果查询抛出异常,但没有执行 countDown(),计数器就可能一直大于 0,主线程将永久等待。

  1. 结果容器必须考虑并发安全。

多个线程同时写入结果时,使用 ConcurrentHashMap 比普通 HashMap 更合适。这里每个任务写入不同的键,也可以使用 AtomicReference、独立结果对象或 Future 等方式收集结果。

  1. 查询异常不能被忽略。

上面的代码能保证异常发生时计数器递减,但 submit() 返回的 Future 没有被检查,因此任务内部的异常可能被隐藏,最终返回一个缺少数据的结果。

生产代码应该明确异常处理策略,例如:某个查询失败就让整个接口失败,或者允许非关键数据缺失。不能只保证线程结束,却不检查业务结果是否完整。

java 复制代码
import java.util.concurrent.*;
import java.util.*;

public class OrderQueryService {

    // 独立线程池,实际项目建议通过 Spring 配置管理
    private final ExecutorService executor =
            Executors.newFixedThreadPool(4);

    public Map<String, Object> queryOrderDetail(Long orderId) {
        // 1. 初始化计数器:4 个并行查询任务
        CountDownLatch latch = new CountDownLatch(4);

        // 2. 使用线程安全的结果容器
        Map<String, Object> result = new ConcurrentHashMap<>();

        // 3. 提交订单基本信息查询
        executor.submit(() -> {
            try {
                result.put("order", queryOrder(orderId));
            } finally {
                latch.countDown();
            }
        });

        // 4. 提交订单明细查询
        executor.submit(() -> {
            try {
                result.put("details", queryDetails(orderId));
            } finally {
                latch.countDown();
            }
        });

        // 5. 提交供应商信息查询
        executor.submit(() -> {
            try {
                result.put("supplier", querySupplier(orderId));
            } finally {
                latch.countDown();
            }
        });

        // 6. 提交物流信息查询
        executor.submit(() -> {
            try {
                result.put("logistics", queryLogistics(orderId));
            } finally {
                latch.countDown();
            }
        });

        try {
            // 7. 等待所有任务执行完毕
            latch.await();
        } catch (InterruptedException e) {
            // 恢复中断标记,避免吞掉中断信号
            Thread.currentThread().interrupt();
            throw new RuntimeException("查询订单详情被中断", e);
        }

        // 8. 所有查询结束后统一返回
        return result;
    }

    // 以下方法仅用于演示,替换为实际的 DAO / Service 查询
    private Object queryOrder(Long orderId) {
        return "订单基本信息";
    }

    private Object queryDetails(Long orderId) {
        return "订单明细";
    }

    private Object querySupplier(Long orderId) {
        return "供应商信息";
    }

    private Object queryLogistics(Long orderId) {
        return "物流信息";
    }
}
相关推荐
谢尔登1 小时前
02_开发环境配置
java
学长毕设1 小时前
基于springboot的实验室设备管理系统
java·spring boot·后端
H.莓飛2 小时前
【数据结构】排序总结与规模实测
linux·服务器·开发语言·数据结构·visual studio·1024程序员节
学长毕设2 小时前
基于springboot的社区养老监护系统的设计与实现
java·spring boot·后端
会编程的吕洞宾2 小时前
RAG检索质量怎么评估?3个指标+LLM裁判实战
java·开发语言·人工智能·后端
卢总霸气2 小时前
上海交大机动学院学位论文参考文献Zotero CSL 样式-2026
java
FLJwu2 小时前
运动相机采购实战|第四篇(终章):一张清单走完选型全流程,把风险拦在源头
c语言·开发语言·数码相机·运动相机·oem 量产
收纳过往2 小时前
数据库加索引的时机:慢查询日志说话
android·开发语言·数据库·python·低代码·rxjava