exchanger交换大对象的核心优势是零拷贝,只交换对象引用而非副本,避免内存复制、序列化和gc压力;双方须约定缓冲区所有权转移规则,配合超时机制与专用线程使用。

Exchanger 交换大对象时,核心优势就是“零拷贝”——它只交换对象引用,不复制内容。这对 byte[]、List、自定义缓冲区等大内存结构特别友好,只要双方约定好所有权转移规则,就能高效流转。
用引用交换代替数据复制
大对象(比如几 MB 的 byte[])如果走传统队列或手动赋值,会触发大量内存分配和 GC 压力。Exchanger 直接传递引用,交换瞬间完成:
- 线程 A 填满 backBuffer 后调用 exchanger.exchange(backBuffer),不 new、不 clone、不序列化
- 线程 B 收到的就是同一块堆内存地址,无需再解析或重建
- 交换后,backBuffer 的使用权立即移交——A 不再读写它,B 开始处理;下一轮 A 用另一块 buffer 或复用前一块(需清空)
避免缓冲区误用和状态混乱
交换的是引用,不是副本,所以双方必须明确“谁负责重置、谁负责释放”:
- 不要在交换后继续往 received 对象里写数据——它已归属对方
- 建议使用固定大小的双缓冲池(bufferA / bufferB),两个线程循环复用,避免频繁 new
- 若用单 buffer,交换前必须确保已写完;交换后接收方要主动清空或标记为可用,否则下次交换可能带脏数据
超时 + 非 null 初始化是稳定前提
大对象交换往往耗时更敏感,卡住后果更严重:
- 永远不用无参 exchange(data),改用 exchange(data, 2, TimeUnit.SECONDS)
- 超时抛 TimeoutException,不是返回 null —— 捕获后应记录日志、释放当前 buffer 引用(防止内存泄漏)、决定是否重试
- 初始化时传哑值(如 new byte[0] 或 new ArrayList()),避免某一方传 null 导致对面 NPE
绑定专用线程,杜绝配对错乱
大对象处理通常耗时长、上下文重,不能依赖线程池动态调度:
- 启动时创建两个专用线程(如 RenderThread 和 DisplayThread),各自持有一个 Exchanger 实例
- 禁止多个任务共用同一个 Exchanger 实例——第三个线程调用会无限等待,或与错误线程配对
- 线程退出前,确保已执行完最后一次 exchange;若中途异常退出,另一方靠超时机制及时脱困
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











