在多线程编程中,线程编排(Thread Coordination)是协调多个线程执行顺序、处理依赖关系、同步结果的关键技术。以下是常见的线程编排方法及实践指南:
一、核心线程编排场景
- 顺序依赖:线程B必须等待线程A完成
- 并行聚合:多个线程并行执行,主线程等待所有结果
- 分阶段执行:任务分多个阶段,阶段间需同步
- 结果传递:线程间需要传递数据或状态
二、线程编排工具与实现
1. 基础同步工具类
-
CountDownLatch
作用:主线程等待一组子线程完成
示例:CountDownLatch latch = new CountDownLatch(3); // 初始化计数器为3 ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 3; i++) { executor.execute(() -> { try { // 执行任务... } finally { latch.countDown(); // 计数器减1 } }); } latch.await(); // 阻塞直到计数器归零 executor.shutdown(); -
CyclicBarrier
作用:多线程相互等待到指定屏障点后继续执行
示例:CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("所有线程到达屏障点"); // 可选回调 }); for (int i = 0; i < 3; i++) { new Thread(() -> { try { // 执行阶段1任务... barrier.await(); // 等待其他线程 // 执行阶段2任务... } catch (Exception e) { e.printStackTrace(); } }).start(); }
2. Future与CompletableFuture
-
Future
作用:获取异步任务结果
示例:ExecutorService executor = Executors.newFixedThreadPool(2); Future<Integer> future1 = executor.submit(() -> 100); Future<Integer> future2 = executor.submit(() -> 200); int sum = future1.get() + future2.get(); // 阻塞等待结果 -
CompletableFuture(推荐)
作用:链式编排异步任务,支持组合、异常处理
示例:CompletableFuture<Integer> task1 = CompletableFuture.supplyAsync(() -> 100); CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> 200); // 合并两个任务结果 CompletableFuture<Integer> combined = task1.thenCombine(task2, Integer::sum); // 处理最终结果 combined.thenAccept(result -> System.out.println("Total: " + result)) .exceptionally(ex -> { System.err.println("Error: " + ex.getMessage()); return null; });
3. Fork/Join框架
作用:递归分解大任务为小任务并行处理
示例(计算1~n的和):
class SumTask extends RecursiveTask<Long> {
private final int start;
private final int end;
SumTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if (end - start <= 1000) { // 阈值内直接计算
long sum = 0;
for (int i = start; i <= end; i++) sum += i;
return sum;
} else { // 拆分任务
int mid = (start + end) / 2;
SumTask left = new SumTask(start, mid);
SumTask right = new SumTask(mid + 1, end);
left.fork();
return right.compute() + left.join();
}
}
}
// 使用
ForkJoinPool pool = new ForkJoinPool();
long result = pool.invoke(new SumTask(1, 1000000));
4. Phaser(高级场景)
作用:更灵活的屏障控制,支持动态注册/注销线程
示例:
Phaser phaser = new Phaser(1); // 主线程注册
for (int i = 0; i < 3; i++) {
phaser.register(); // 注册子线程
new Thread(() -> {
phaser.arriveAndAwaitAdvance(); // 等待所有线程到达阶段1
// 执行阶段1任务...
phaser.arriveAndDeregister(); // 阶段1完成,注销
}).start();
}
phaser.arriveAndAwaitAdvance(); // 主线程等待所有子线程完成阶段1
三、编排模式与最佳实践
1. 常见编排模式
| 模式 | 适用场景 | 工具选择 |
|---|---|---|
| 串行依赖 | A→B→C任务链 | CompletableFuture.thenRun() |
| 并行聚合 | 同时执行多个任务,汇总结果 | CompletableFuture.allOf() |
| 分阶段执行 | 多阶段任务(如初始化→计算→输出) | CyclicBarrier或Phaser |
| 动态任务链 | 根据前序结果决定后续任务 | CompletableFuture.thenCompose() |
2. 最佳实践
-
避免阻塞主线程
使用异步回调(如thenAccept)替代同步get()调用。 -
异常处理
通过exceptionally或handle方法统一处理异常:CompletableFuture.supplyAsync(() -> { // 可能抛出异常的任务 }).exceptionally(ex -> { System.err.println("Task failed: " + ex.getMessage()); return defaultValue; }); -
资源控制
- 限制并行度:使用固定大小线程池
- 超时控制:
future.get(5, TimeUnit.SECONDS)
-
性能优化
- 任务拆分粒度适中(避免过多线程切换)
- 使用无锁数据结构(如
ConcurrentHashMap)
四、复杂编排示例
场景:电商订单处理流程
- 并行校验库存和用户风险
- 两者都通过后生成订单
- 异步通知物流系统
实现:
// 1. 并行校验库存和用户
CompletableFuture<Boolean> checkStock = CompletableFuture.supplyAsync(
() -> inventoryService.checkStock(itemId), stockPool);
CompletableFuture<Boolean> checkRisk = CompletableFuture.supplyAsync(
() -> riskService.checkUserRisk(userId), riskPool);
// 2. 两者都通过后生成订单
CompletableFuture<Order> createOrder = checkStock
.thenCombine(checkRisk, (stockOk, riskOk) -> {
if (stockOk && riskOk) {
return orderService.createOrder(userId, itemId);
} else {
throw new RuntimeException("校验未通过");
}
});
// 3. 异步通知物流
createOrder.thenAcceptAsync(order ->
logisticsService.notify(order.getId()), logisticsPool);
// 4. 异常统一处理
createOrder.exceptionally(ex -> {
log.error("订单创建失败", ex);
return null;
});
五、总结
- 简单依赖:优先使用
CompletableFuture链式调用 - 并行聚合:
CompletableFuture.allOf()或CountDownLatch - 分阶段控制:
CyclicBarrier或Phaser - 递归分治:
Fork/Join框架
通过合理选择工具和模式,可以实现高效、清晰的多线程编排,同时需注意避免死锁、资源竞争和线程泄漏问题。
1501

被折叠的 条评论
为什么被折叠?



