实现连接池:Java

2022-09-02 09:29:49

在我面临的一次面试中,我被要求实施连接池。所以方法是这样的:

  1. 创建或ListHashMap
  2. 创建预定义的连接数
  3. 将它们添加到集合中。
  4. 现在,当调用类的方法时,将返回一个连接引用。ConnectionImplgetConnection()ConnectionPoolingImpl

现在,当有人返回连接()时,我如何确保当同一应用程序再次尝试重用连接对象时,我的实现会引发异常?releaseConnection(ConnectionImpl O)

相同的连接对象可能已返回到新应用程序,并且应该能够使用它。

我的观点是在每个对象的另一种数组结构中维护一个标志变量,并将该变量设置为有效值。当用户返回连接对象时,我会使该值无效。对于 my 中的每个操作,我都必须验证用户是否具有有效的标志。ConnectionimplConnectionImpl

你对这种方法有什么看法?


答案 1

我不会从池中返回“真正的”连接对象,而是返回一个包装器,它为提供了连接生命周期的控制,而不是客户端。

假设您有一个非常简单的连接,您可以从中读取值: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

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);
    }
}

这里的基本思想是可以的,但是我们无法确定连接是否返回,并且我们无法确定它们是否已关闭然后返回,或者您没有返回完全来自另一个源的连接。

解决方案(当然)是另一层间接层:创建一个池,该池返回一个包装器,该包装器在调用时不关闭底层连接,而是将其返回到池中:Connectionclose()

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();
                }
            }
        };
    }

}

这将是客户端代码唯一能看到的东西。假设它是 的唯一所有者,这种方法有几个优点:ConnectionPoolStreamConnectionPool

降低了复杂性,对客户端代码的影响最小 - 自行打开连接和使用池之间的唯一区别是使用工厂来获取 s(如果您使用的是依赖关系注入,则可能已经这样做了)。最重要的是,您始终以相同的方式清理资源,即通过调用 .就像你不在乎什么一样,只要它给你你需要的数据,你就不在乎什么,只要它释放了你声称的资源。您不必考虑此连接是否来自池。Connectionclose()readclose()

防止恶意/错误使用 - 客户端只能返回从池中检索到的资源;他们无法关闭底层连接;他们不能使用他们已经返回的连接...等。

资源的“保证”返回 - 由于我们的实现,即使所有对借入的引用都丢失了,它仍然会返回到池中(或者至少有机会被返回)。当然,这种连接将保持比必要的时间更长 - 可能是无限期的,因为最终确定不能保证运行 - 但这是一个小小的改进。finalizeConnection


答案 2

我只是告诉他们我会使用H2附带的类(这里)(你可以把它复制出来)。螺丝钉试图实现一个:)这可能是一个棘手的问题。JdbcConnectionPool


推荐