编辑
🌟 大家好,我是摘星! 🌟
今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
目录
半同步/半异步(Half-Sync/Half-Async)
问题:为什么需要半同步/半异步模式?
在现代高并发系统中,我们常常面临一个核心矛盾:
- 高吞吐量需求:需要快速响应大量请求(如Web服务器、即时通讯)。
- 复杂业务逻辑:某些任务必须阻塞执行(如数据库查询、文件IO)。
传统的纯同步(如每请求一线程)会导致线程爆炸,而纯异步(如Reactor模式)对复杂业务不友好。
半同步/半异步模式应运而生——它通过分层架构,结合两者的优势:
- 异步层:用单线程+非阻塞IO处理高并发接入(如NIO)。
- 同步层:用线程池执行阻塞任务(如业务逻辑)。
- 队列层:作为缓冲,平衡两者速度差异。
本文将通过核心原理、Java代码实战、对比分析,带你彻底掌握这一经典架构模式。
1. 核心原理深度拆解
1.1. 三明治架构(分层设计)
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ Async Layer │───> │ Task Queue │───> │ Sync Layer │ │ (Non-Blocking)│<─── │ (Thread-Safe) │<─── │ (Thread Pool) │ └───────────────┘ └───────────────┘ └───────────────┘
- 异步层:使用 单线程 + Selector 处理高并发I/O(如NIO)
- 队列层:使用 BlockingQueue 实现流量缓冲(容量需根据业务设定)
- 同步层:使用 线程池 处理阻塞任务(如数据库操作)
1.2. 吞吐量优化关键
- 异步层不等待:接收到请求后立即转交队列,快速回到I/O处理
- 同步层可控:通过线程池大小限制并发任务数,避免资源耗尽
2. 生活化类比:快递分拣系统
系统组件 |
现实类比 |
核心行为 |
异步层 |
快递扫描机 |
快速扫描包裹,不拆包检查内容 |
队列层 |
传送带缓冲区 |
暂存包裹,平衡上下游速度差异 |
同步层 |
分拣工人团队 |
拆包检查、分类处理包裹 |
- 突发流量处理:扫描机(异步层)1秒处理1000包裹 → 传送带(队列)缓冲 → 工人(同步层)按能力处理
3. Java代码实现
import java.util.concurrent.*; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; public class HalfSyncHalfAsyncServer { // 任务队列(设置容量防止OOM) private final BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>(1000); // 异步层:模拟NIO事件循环 class AsyncLayer implements Runnable { private Selector selector; public AsyncLayer() throws Exception { this.selector = Selector.open(); // 初始化ServerSocketChannel等(略) } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { try { // 1. 非阻塞select() int readyChannels = selector.selectNow(); if (readyChannels == 0) continue; // 2. 处理IO事件(如新连接) SocketChannel client = acceptNewClient(); // 伪代码 System.out.println("[Async] 接收新连接: " + client); // 3. 生成任务并提交队列 taskQueue.put(() -> handleClient(client)); } catch (Exception e) { e.printStackTrace(); } } } private void handleClient(SocketChannel client) { // 实际业务处理在同步层 System.out.println("[Sync] 处理客户端: " + client); // 模拟耗时操作 try { Thread.sleep(1000); } catch (InterruptedException e) {} } } // 同步层:线程池配置 private final ExecutorService syncWorkerPool = new ThreadPoolExecutor( 4, // 核心线程数(根据CPU核数调整) 16, // 最大线程数(突发流量缓冲) 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), new ThreadFactory() { private int count = 0; @Override public Thread newThread(Runnable r) { return new Thread(r, "sync-worker-" + count++); } }, new ThreadPoolExecutor.CallerRunsPolicy() // 队列满后由提交线程执行 ); // 启动方法 public void start() throws Exception { // 启动异步层线程 new Thread(new AsyncLayer()).start(); // 启动同步层消费队列 new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { try { Runnable task = taskQueue.take(); syncWorkerPool.execute(task); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }).start(); } public static void main(String[] args) throws Exception { new HalfSyncHalfAsyncServer().start(); } }
关键配置说明:
// 线程池拒绝策略:当队列满时,由提交任务的线程自己执行 new ThreadPoolExecutor.CallerRunsPolicy() // 队列选择:LinkedBlockingQueue vs ArrayBlockingQueue // - LinkedBlockingQueue:默认无界,需设置容量 // - ArrayBlockingQueue:固定大小,更严格的控制
4. 核心优势
4.1. 线程模式对比
模式 |
适用场景 |
吞吐量 |
资源消耗 |
编程复杂度 |
Half-Sync/Half-Async |
高并发+阻塞任务混合 |
高 |
中 |
中 |
Thread-Per-Request |
简单业务逻辑 |
低 |
高 |
低 |
Leader/Follower |
均匀负载分配 |
中高 |
中 |
高 |
Reactor |
纯非阻塞任务 |
极高 |
低 |
高 |
4.2. 队列策略对比
队列类型 |
特点 |
适用场景 |
LinkedBlockingQueue |
无界队列(需设置容量) |
平稳流量,允许短期堆积 |
SynchronousQueue |
直接传递,无缓冲 |
严格要求实时处理 |
PriorityBlockingQueue |
按优先级处理任务 |
VIP用户请求优先 |
DelayedWorkQueue |
延迟执行任务 |
定时任务调度 |
5. 优化扩展
5.1. 异步层性能提升
// 使用多个Selector(多线程异步层) Selector[] selectors = new Selector[4]; for (int i = 0; i < selectors.length; i++) { selectors[i] = Selector.open(); new Thread(new AsyncLayer(selectors[i])).start(); }
5.2. 同步层动态扩缩容
// 动态调整线程池参数 ThreadPoolExecutor pool = (ThreadPoolExecutor) syncWorkerPool; pool.setCorePoolSize(8); // 根据系统负载动态调整 pool.setMaximumPoolSize(32);
5.3. 监控关键指标
// 队列积压监控 int queueSize = taskQueue.size(); // 线程池活跃度 int activeCount = pool.getActiveCount(); long completedTasks = pool.getCompletedTaskCount();
结语:如何优雅落地半同步/半异步模式?
半同步/半异步模式并非银弹,使用时需注意:
- 队列管理:设置合理容量,避免OOM;支持优先级/超时控制。
- 线程池调优:根据任务类型(CPU/IO密集型)动态调整线程数。
- 监控告警:关注队列积压、线程池活跃度等关键指标。
适用场景:
✅ 高并发+长耗时任务混合(如电商下单系统)
✅ 需要平衡吞吐量与开发效率
不适用场景:
❌ 纯计算密集型任务(建议用分治+Future)
❌ 超低延迟场景(建议用纯异步模式)