exchanger是专为两个线程严格配对、原子交换对象引用的无锁同步工具,要求双方同时调用exchange()才能完成交换,不支持多线程、不缓存不广播,典型应用为双缓冲复用。

Exchanger 不是通道,也不是消息队列或共享内存,它是一个**配对阻塞点**——两个线程必须同时到达、交换引用、各自离开。它的“安全”来自原子性配对和无锁设计,而非传输保障或数据复制。
它只做一件事:严格配对交换
Exchanger 的行为像一扇只能两人同时推拉的门:一方推门(调用 exchange())后必须等另一方从对面推,门才打开并完成交换;没人来推,就一直等着。这种机制天然排除了竞态、无需 synchronized 或 volatile 修饰交换对象本身。
- 每次调用都是一次独立的“手拉手”动作,不缓存、不重放、不广播
- 交换的是对象引用,不是内容拷贝——速度快,但要求双方对同一对象的读写有明确分工(例如双缓冲场景中,A 写完交出,B 读完清空)
- 类型由泛型固定(如
Exchanger<byte></byte>),编译期就防止错类型混用
必须用超时,否则风险不可控
生产环境绝不能依赖无参 exchange(x)。一个线程卡住(如处理异常、GC 停顿、逻辑阻塞),另一个就会无限等待,拖垮整个协作流程。
- 推荐统一使用
exchange(x, 3, TimeUnit.SECONDS)这类带超时的签名 - 捕获
TimeoutException后应主动退出循环、记录告警、释放资源(如关闭缓冲区池) - 若业务允许降级,可设默认值返回(如返回空数组),避免线程僵死
典型安全模式:双缓冲复用
这是 Exchanger 最稳妥的落地方式——用两块固定内存反复交换,避免频繁分配与 GC 压力,同时靠“谁持有谁写”的约定杜绝脏读。
- 初始化两个 buffer:
byte[] bufA = new byte[4096]; byte[] bufB = new byte[4096]; - 线程 A 每次填满当前 buffer 后调用
exchanger.exchange(buf),拿到 B 就继续填;线程 B 同理 - 关键约束:任一时刻,每个 buffer 只被一个线程写,另一个线程只读(且读完即清空或重置)
- 若需保证读端看到完整写入,应在 buffer 内部加 volatile 标志位或用
AtomicInteger记录有效长度
它不解决的问题,得你来补
Exchanger 自身不提供内存可见性延伸、资源生命周期管理、错误恢复或负载均衡能力。这些必须由上层逻辑兜底:
- 交换大对象后,对方线程首次访问字段前,需自行插入同步措施(如
synchronized块或VarHandlefence) - 若某次交换失败(超时/中断),要判断是否需要重试、丢弃数据或触发熔断
- 不能指望它绑定 CPU 核心——亲和性需通过
Thread.setAffinity()(Linux)或第三方库控制











