跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

什么是Java中的数据交换(Exchanger)_线程间配对交换数据的场景

Exchanger 是专为两两线程严格同步交换引用值设计的协作工具,适用于双缓冲日志、乒乓内存池等成对数据交换场景,不支持多线程广播或队列式共享。 Exchanger 是什么,适合解决哪类线程协作问题 它不是用来“广播”或“共享”数据的,而是专为**两两配对、严格同步交换**设计的。典型场景:一个线程生产缓冲区 A,另一个线程消费并填充缓冲区 B,双方在边界点互换引用——比如双缓冲日志写入、乒乓式内存池复用。 常见错误是把它当
BlockingQueue
或
Phaser
用:想让多个线程任意交换,结果线程卡死在
exchange()
上,因为 Exchanger 只匹配成对调用,多出的线程会一直阻塞。 必须且仅需两个线程参与一次交换;第三个线程调用
exchange()
会等待前两者之一再次进入 不支持超时的配对(除非用带
timeout
的重载),没有“放弃交换”的内置机制 交换的是**引用值**,不是拷贝——双方拿到的是对方传入的同一对象,注意线程安全边界 怎么正确调用 exchange(),参数和返回值意味着什么
exchange()
的签名是
V exchange(V x)
,传入的是“我手上的数据”,返回的是“对方给我的数据”。它不是 void 方法,返回值必须被接收,否则交换就白做了。 容易忽略的是:即使发生中断,
exchange()
也可能已完成交换才抛
InterruptedException
。所以不能假设“抛异常 = 没交换”。 立即学习 “ Java免费学习笔记(深入) ”; 传
null
是合法的,但双方都传
null
就真交换了个空引用,别误以为是初始化信号 如果一方先调用
exchange(v1)
进入等待,另一方调用
exchange(v2)
,则前者返回
v2
,后者返回
v1
若超时版本
exchange(v, timeout, unit)
返回
null
,只代表超时未配对,不代表对方没调用——可能对方刚进、还没到配对点 Exchanger 在实际代码里怎么避免死锁和资源泄漏 最常踩的坑是:线程在交换前已持有锁,或交换后没清理状态,导致后续逻辑卡住。Exchanger 本身无锁,但它的使用上下文极易引入锁竞争。 Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 例如用 Exchanger 协调两个工作线程交换
ByteBuffer
,但忘了重置
position
和
limit
,下一轮读写直接越界或跳过数据。 交换前后务必做状态重置(如 buffer 的
clear()
、集合的
clear()
),别依赖“对方会清” 不要在 synchronized 块里调用
exchange()
,否则可能造成锁顺序反转+等待链 如果某线程因异常退出,没走到下一次
exchange()
,它的配对线程就会永久阻塞——需配合
Thread.interrupt()
或超时机制兜底 Exchanger 和其他同步工具的关键区别在哪 它和
CyclicBarrier
看似都等“人齐”,但语义完全不同:
CyclicBarrier
是大家到达后一起往下走;
Exchanger
是两人面对面交货,交完各走各路,没有“下一步统一动作”。 也别和
TransferQueue
混:后者支持单向传递、可选阻塞/非阻塞、能排队,而 Exchanger 是硬性双向、强配对、无队列。
Exchanger
内部用的是 CAS + 自旋 + 队列节点,高争用下比纯锁轻量,但比无竞争的本地变量开销大得多 Java 9+ 中它仍无结构化并发支持(比如无法绑定
StructuredTaskScope
),异常传播需手动处理 调试时看不到“谁在等谁”,只能靠日志打点或 JFR 事件跟踪
exchange()
调用栈 配对这件事,看着简单,但一旦涉及生命周期管理、异常路径、状态一致性,就很容易漏掉一环。别指望它自动兜底。

相关文章