exchanger不是缓冲区,仅提供两个线程在汇合点安全交换缓冲区引用的机制;必须严格一对一配对,不支持多线程轮换,需配合缓冲区复用、状态管理及带超时的exchange方法使用。

Exchanger 不是缓冲区,也不提供缓冲能力;它只是让两个线程在确定的汇合点安全交换缓冲区引用。真正的“同步缓冲”效果,来自线程对缓冲区的复用 + Exchanger 的配对机制 + 显式状态管理,三者缺一不可。
必须严格一对一配对,不能多线程轮换
Exchanger 只支持两个线程同时参与一次交换。第三个线程调用 exchange() 会永久阻塞——因为它内部没有等待队列,也不做轮询或重试。常见误用包括:
- 用
FixedThreadPool(2)提交多个任务:线程被复用后,上一轮未完成的等待可能残留,导致配对错乱 - 启动两个生产者线程:双方都在等“唯一的消费者”,结果双双挂起,jstack 显示停在
Exchanger$Node.block - 每次新建
Exchanger实例:破坏共享状态,交换根本无法成立
缓冲区生命周期要闭环管理
Exchanger 只交换引用,不复制数据,也不重置缓冲区状态。因此,每轮交换前后,线程必须主动维护缓冲区的 position、limit 等状态:
- 生产者侧典型流程:
fillData(buf) → buf.flip() → exchanger.exchange(buf) → buf.clear() - 消费者侧典型流程:
buf = exchanger.exchange(null) → process(buf) → buf.clear()(或compact(),视是否保留未读数据而定) - 切忌在
exchange()前后插入耗时操作(如日志打印、磁盘写入),否则会拉长等待窗口,掩盖真实瓶颈
推荐统一使用带超时的 exchange 方法
无限等待的 exchange(V) 在生产环境风险高。应始终采用带超时版本,把超时当作故障信号而非兜底手段:
-
exchanger.exchange(buf, 5, TimeUnit.SECONDS)—— 超时抛TimeoutException,可触发告警或降级处理 - 捕获
InterruptedException后应清理资源并退出循环,避免线程处于不可预期状态 - 超时时间需结合业务单次处理耗时设定,不宜过短(频繁误报)或过长(故障响应迟钝)
泛型类型必须一致,且缓冲区应长期持有
声明时必须明确泛型,例如 Exchanger<bytebuffer></bytebuffer>。若一个线程传 ByteBuffer,另一个传 byte[],运行时强转会失败:
- 双方共用同一缓冲区类型(都用
ByteBuffer或都用byte[]) - 缓冲区实例应在各自线程内创建并长期持有,避免每轮
new新对象,减少 GC 压力 - 避免传
null,除非双方明确约定该值为合法占位符











