exchanger 是一个线程间同步点,需两个线程同时调用 exchange() 才能交换数据,适用于生产者-消费者、双缓冲等成对协作场景,不支持多线程异步等待,误用易致永久阻塞。

Exchanger 是什么,什么时候该用它
它不是线程安全容器,也不是锁,而是一个线程间“一手交钱一手交货”的同步点:两个线程必须同时到达 exchange() 才能完成数据交换,缺一不可。适合成对协作的场景,比如生产者-消费者模型中,一个线程填满缓冲区后必须等另一个线程取走才能继续;或者双缓冲渲染里前后帧数据交换。
别拿它替代 BlockingQueue 或 AtomicReference——前者支持多线程、异步等待,后者适合单值共享。用错场景会导致线程永久阻塞。
调用 exchange() 时线程卡住不动了
这是最常见现象:一个线程执行完 exchange(v) 就停在那儿,CPU 占用低,日志也不报错。原因只有一个:另一个线程根本没调用 exchange(),或调用时机错开(比如异常退出、提前 return、被中断但没处理)。
- 务必确保两个线程都进入同一对
exchange()调用,且生命周期可预期 - 加超时是刚需:
exchange(v, 5, TimeUnit.SECONDS),否则单点失败会让整个协作链路挂死 - 捕获
InterruptedException后记得恢复中断状态:Thread.currentThread().interrupt(),否则上层可能收不到中断信号
Exchanger 的泛型和类型安全怎么保障
它本身是 Exchanger<v></v>,但泛型只在编译期起作用。运行时如果两个线程传入不同类型的对象(比如一个传 String,一个传 byte[]),不会报错,但接收方强转会触发 ClassCastException。
立即学习“Java免费学习笔记(深入)”;
实际写法要统一契约:
- 定义好交换的数据结构,比如
Exchanger<bufferwrapper></bufferwrapper>,而不是裸用Exchanger<object></object> - 避免在 exchange 前后做隐式转换(如把
int包装成Integer再拆箱),容易因自动装箱/拆箱引入空指针或类型不匹配 - 如果必须支持多种类型,用 sealed class 或枚举字段区分 payload 类型,而非靠泛型欺骗编译器
性能和线程复用要注意什么
Exchanger 内部用的是无锁算法(CAS + 队列),单次交换开销很低,但它的吞吐量受限于“配对延迟”——两个线程到达时间差越大,平均等待越长。
所以:
- 不要在线程池里随意扔任务去调用
exchange(),线程复用会导致“旧线程还在等上次配对,新任务又来了”,出现错配或超时 - 固定配对关系更稳:比如启动时就创建两个专用线程,各自循环调用
exchange(),中间用局部变量缓存上下文 - JDK 9+ 对
Exchanger做了优化,但低于 JDK 8 的版本在高竞争下可能出现自旋浪费,压测时注意 CPU 使用率是否异常升高
真正难的不是写对那几行代码,而是让两个线程在时间上严丝合缝地碰头——这要求你对它们的执行节奏、生命周期、错误恢复都有明确控制,而不是依赖“应该会碰到”的侥幸。









