资源生命周期与 AutoCloseable
资源所有权与 AutoCloseable:谁创建谁关闭、跨层传递资源时的生命周期纪律。
#concept
#growing
#dev
#java
[!info] related notes
资源生命周期与 AutoCloseable
这个概念为什么出现
资源泄漏是生产慢性病:连接耗尽、文件句柄打满。技术机制是 close,工程机制是所有权:谁创建,谁负责释放;谁借用,谁按契约归还。
[!abstract] 一句话理解
AutoCloseable标出可关闭资源;生命周期纪律是“所有权清晰 + 异常路径也释放 + 池化资源按池契约归还”。
最小可运行示例
场景:单线程持有计数型资源,重复 close 只计数一次
import java.util.concurrent.atomic.AtomicInteger;
final class Lease implements AutoCloseable {
private static final AtomicInteger OPEN = new AtomicInteger();
private boolean closed;
Lease() { OPEN.incrementAndGet(); }
static int openCount() { return OPEN.get(); }
@Override public void close() {
if (!closed) {
closed = true;
OPEN.decrementAndGet();
}
}
}
public class LeaseDemo {
static void use() {
try (Lease lease = new Lease()) {
System.out.println("in-use=" + Lease.openCount());
}
}
public static void main(String[] args) {
use();
System.out.println("after=" + Lease.openCount());
}
}
建议运行:
javac LeaseDemo.java && java LeaseDemo
期望输出:
in-use=1
after=0
结合场景再看三个关注点
- 本例 close 在单线程下幂等;OPEN 是原子计数器,但 closed 不是原子检查更新,同一个 Lease 不支持并发关闭。\
- 作用域结束即释放,不要把短生命周期资源塞进长期字段。
- 池化对象的 close 可能是归还,不是物理销毁——读文档。
核心概念与准确模型
1. 所有权转移
| 模式 | 责任 |
|---|---|
| 工厂返回资源 | 文档写清由调用方关闭 |
| 方法内打开且不转移所有权 | 方法内 TWR 关闭 |
| 借用调用方传入的资源 | 按约定使用,不默认取得关闭权 |
| 依赖注入单例客户端 | 应用停止时关闭 |
2. Closeable vs AutoCloseable
- Closeable 继承 AutoCloseable,close 可抛 IOException,且要求关闭已关闭的流没有效果(幂等)。\
- AutoCloseable.close 可抛 Exception,接口本身不要求幂等,虽建议实现者尽量提供幂等关闭。\
3. 与优雅关闭关系
进程退出前要关闭线程池、服务端 socket、数据源——见服务工程。
边界情况与反直觉行为
- 关闭顺序:先依赖方后底层。
- 若 TWR 结束后异步回调才使用资源,会读到已关闭对象;必须把资源生命周期覆盖到异步任务真正完成,或明确转移所有权。\
- 异常构造半对象时注意已获取资源回滚。
常见误区
[!warning] 常见误区:连接池拿来的 Connection 不关闭 必须 close/归还,否则池耗尽。
工程实践
- API 文档写清 ownership。
- 静态分析/实验检测泄漏。
- 超时与取消路径也要释放。
- 测试用计数/代理断言 close 次数。
本节总结
- 资源=所有权问题
- AutoCloseable + TWR 是机制
- 池化与进程级关闭是工程延伸
自测题
- 为什么 close 最好幂等?
- 方法返回 InputStream 时谁关闭?
参考答案
- 防御双重关闭与错误路径重复清理。
- 若工厂转移所有权,通常调用方关闭;借用的共享流则按契约处理。文档不清会增加泄漏或过早关闭风险。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| AutoCloseable | API | 契约 |
| Closeable | API | IO 关闭 |