Exchanger 不管理资源生命周期,资源移交后由接收方负责关闭;应使用 try-with-resources 确保及时释放,避免传递 DataSource 等长期对象,并在超时或中断时妥善处理悬空资源。

Exchanger 本身不持有数据库连接池或文件流资源,它只是一个用于线程间交换数据的同步工具。所谓“在 Exchanger 中关闭资源”,实际是指:当两个线程通过 Exchanger 传递了 Connection、DataSource、InputStream 等资源对象后,**哪一方负责关闭、何时关闭、如何避免重复关闭或漏关**——这才是关键。
明确资源归属,谁获取谁释放
Exchanger 只做数据交换,不参与生命周期管理。一旦线程 A 将一个 Connection 实例交给线程 B,该 Connection 的使用和关闭责任就完全转移到线程 B。线程 A 不得再访问或关闭它;线程 B 必须在业务逻辑结束后主动释放。
- 错误做法:线程 A 在 exchange() 后立刻 close() 自己传出去的连接(已移交,再 close 会抛异常或破坏连接池状态)
- 正确做法:线程 B 收到连接后,在 try-with-resources 或 finally 块中确保关闭;若使用连接池(如 HikariCP),调用 close() 即归还,不是销毁
- 特别注意:不要在线程 A 的 exchange() 调用前后对同一连接做 open/close 操作,易引发 ClosedConnectionException
用 try-with-resources 包裹交换后的资源
线程 B 在拿到资源后,应立即纳入自动资源管理范围,而不是手动延迟关闭。
- 例如接收一个 PreparedStatement:
stmt.executeUpdate();
} catch (SQLException | InterruptedException e) {
throw new RuntimeException(e);
}
- 若需同时处理 ResultSet 和 Connection,可嵌套声明(按后开先关顺序):
PreparedStatement stmt = conn.prepareStatement("...");
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) { /* 处理 */ }
} catch (SQLException | InterruptedException e) { /* ... */ }
避免在 Exchanger 中直接传递裸连接池实例
DataSource(如 HikariDataSource)是线程安全、长期存活的对象,不应被 exchange 传递。传递它既无必要,也容易导致误关或共享状态混乱。
立即学习“Java免费学习笔记(深入)”;
- 推荐只交换短期使用的资源:Connection、Statement、InputStream、BufferedReader 等
- 若需跨线程复用连接能力,应传递 DataSource 的引用(不 close),由接收方按需 getConnection()
- 切勿在线程 B 中调用 dataSource.close() —— 这会关闭整个连接池,影响全局
超时与中断场景下的资源兜底
exchange() 可能因等待超时或线程中断而返回 null 或抛 InterruptedException。此时要防止资源“悬空”:
- 线程 A 若设置了 timeout,且 exchange() 返回 null,说明对方未接收,原 Connection 仍归 A 所有,必须自行关闭
- 线程 B 若在 exchange() 时被中断,需检查是否已收到资源;若已收到但未处理完,仍要保证最终关闭
- 建议在 exchange 前将资源包装进自定义容器类(如 ResourceHolder<Connection>),附带 isOwned 标志,辅助判断归属


















