最有效、最根本的手段是使用带超时的exchange(V, timeout, unit)。它由Exchanger内部计时器主动唤醒并抛出TimeoutException,使Tomcat工作线程及时释放,避免线程池耗尽;需设合理超时值、捕获异常降级、隔离线程执行,并通过压测验证有效性。

直接用带超时的 exchange(V, timeout, unit) 方法,是防止 Exchanger 引发 Tomcat 线程池耗尽最有效、最根本的手段。不加超时的 exchange(V) 在生产环境极易造成线程长期 BLOCKED,一旦配对线程延迟、异常退出或逻辑卡住,等待线程就会一直挂起,持续占用 Tomcat 工作线程,最终拖垮整个连接池。
必须设置合理超时值
超时时间不是越长越好,也不是拍脑袋定。需结合业务 SLA 和下游依赖响应时间综合判断:
- 若交换用于同步等待另一个模块的计算结果,超时应略大于该模块 P99 响应时间(例如 P99 是 800ms,设为 1200ms)
- 若用于测试或兜底场景,可设为 200–500ms,避免小故障放大成线程雪崩
- 绝对避免设为 0 或 Long.MAX_VALUE —— 这等同于没加超时
捕获并妥善处理 TimeoutException
仅加超时不够,未处理异常仍会导致逻辑中断或掩盖问题:
- 必须显式 catch
TimeoutException,不能只捕获InterruptedException - 超时后建议返回降级数据(如空对象、缓存快照)、记录告警日志,并触发监控上报(如 Prometheus counter +1)
- 避免在 catch 块中重试 exchange 操作——若配对线程已失效,重试只会重复占线程
配合线程生命周期管理
Exchanger 本身不管理线程,但 Tomcat 线程是宝贵的、有上限的资源:
立即学习“Java免费学习笔记(深入)”;
- 禁止在 Tomcat 的请求线程(即 Servlet 线程)中直接调用无保护的
exchange;应封装进异步任务,交由独立线程池执行 - 推荐做法:用
CompletableFuture.supplyAsync(..., customPool)封装交换逻辑,自定义线程池设好 core/max/queue 策略,与 Tomcat 隔离 - 若必须同步等待,确保整个请求链路有全局超时(如 Spring MVC 的
@Timeable或 Feign 的readTimeout),形成超时兜底网
上线前做阻塞注入验证
模拟配对线程延迟或缺失,验证系统是否真正具备容错能力:
- 在测试环境用 Arthas 或 JUnit +
Thread.sleep()故意让一方延迟,观察另一方是否准时抛出TimeoutException并释放线程 - 用 jstack 抓取线程堆栈,确认 BLOCKED 线程数稳定、不随并发增长而线性堆积
- 压测时重点关注 Tomcat
threads.current.busy和executor.active指标,超时配置生效后,这两项应明显收敛


















