CountDownLatch 可以让多个查询任务并行执行,并在所有任务完成后统一汇总结果,从而缩短接口的整体响应时间。
代码中最重要的三个细节
countDown()必须放在finally中。
如果查询抛出异常,但没有执行 countDown(),计数器就可能一直大于 0,主线程将永久等待。
- 结果容器必须考虑并发安全。
多个线程同时写入结果时,使用 ConcurrentHashMap 比普通 HashMap 更合适。这里每个任务写入不同的键,也可以使用 AtomicReference、独立结果对象或 Future 等方式收集结果。
- 查询异常不能被忽略。
上面的代码能保证异常发生时计数器递减,但 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 "物流信息";
}
}