我不会从池中返回“真正的”连接对象,而是一个包装器,它让池控制连接生命周期,而不是客户端。
假设您有一个非常简单的连接,您可以int
从以下位置读取值:
interface Connection {
int read(); // reads an int from the connection
void close(); // closes the connection
}
从流中读取的实现可能如下所示(忽略异常、EOF 处理等):
class StreamConnection implements Connection {
private final InputStream input;
int read(){ return input.read(); }
void close(){ input.close(); }
}
此外,假设您有一个StreamConnection
看起来像这样的 s 池(同样,忽略异常、并发等):
class StreamConnectionPool {
List<StreamConnection> freeConnections = openSomeConnectionsSomehow();
StreamConnection borrowConnection(){
if (freeConnections.isEmpty()) throw new IllegalStateException("No free connections");
return freeConnections.remove(0);
}
void returnConnection(StreamConnection conn){
freeConnections.add(conn);
}
}
这里的基本思想是好的,但是我们不能确定连接是否被返回,我们不能确定它们没有关闭然后返回,或者你没有返回一个完全来自另一个来源的连接.
解决方案(当然)是另一层间接:创建一个返回包装器的池Connection
,而不是在调用时关闭底层连接,而是close()
将其返回到池中:
class ConnectionPool {
private final StreamConnectionPool streamPool = ...;
Connection getConnection() {
final StreamConnection realConnection = streamPool.borrowConnection();
return new Connection(){
private boolean closed = false;
int read () {
if (closed) throw new IllegalStateException("Connection closed");
return realConnection.read();
}
void close() {
if (!closed) {
closed = true;
streamPool.returnConnection(realConnection);
}
}
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
};
}
}
这ConnectionPool
将是客户端代码看到的唯一内容。假设它是 的唯一所有者StreamConnectionPool
,这种方法有几个优点:
降低了复杂性并且对客户端代码的影响最小- 自己打开连接和使用池之间的唯一区别是您使用工厂来获取Connection
s (如果您使用依赖注入,您可能已经这样做了)。最重要的是,您总是以相同的方式清理资源,即调用close()
. 就像你不在乎做什么read
,只要它给你你需要的数据,你不在乎做什么close()
,只要它释放你声称的资源。您不必考虑此连接是否来自池。
防止恶意/错误使用——客户端只能返回他们从池中检索到的资源;他们无法关闭底层连接;他们不能使用他们已经返回的连接......等等。
“保证”返回资源- 由于我们的finalize
实施,即使对借用的所有引用Connection
都丢失了,它仍然会返回到池中(或者至少有机会被返回)。以这种方式,连接当然会比必要的时间更长 - 可能是无限期的,因为不保证最终会运行 - 但这是一个小的改进。