连接池(一):数据库连接关闭后为何还能共享?

在数据库连接池的使用过程中,一直有一个疑问:

在数据库连接的使用过程中,必须要满足独占性要求(其他线程不能访问),使用完成后关闭数据库连接,那么问题来了,数据库都已经连接关闭了,那其他线程又怎么使用呢?

带着这样的疑问,我们一起阅读了“org.apache.commons.dbcp2.BasicDataSource”的实现源码,发现从数据源中获取连接的调用顺序依次如下:
1. BasicDataSource.getConnection();
2. PoolingDataSource.getConnection();
3. GenericObjectPool.borrowObject();
4. PoolableConnectionFactory.makeObject()

现在连接的产生就在于PoolableConnectionFactory是如何产生数据连接的(makeObject),源码如下(为省略篇幅,所以代码为节选):

//  _connFactory为DriverConnectionFactory的实例,创建数据库连接的方法为Driver.connect(),所以这是真正的数据库连接
Connection conn = _connFactory.createConnection();
//  如果此连接创建失败,后面的连接也没用了
if (conn == null) {
    throw new IllegalStateException("Connection factory returned null from createConnection");
}
//  初始化配置代码略
//  返回连接的包装结果
PoolableConnection pc = new PoolableConnection(conn,_pool, connJmxName,
                          _disconnectionSqlCodes, _fastFailValidation);
return new DefaultPooledObject<>(pc);

从这里可以看出,在项目的实际运行过程中,我们从数据库连接池获取的连接类型都是PoolableConnection,其类的声明如下:

public class PoolableConnection extends DelegatingConnection<Connection>
        implements PoolableConnectionMXBean {
}

而DelegatingConnection实现了Connection接口,所以PoolableConnection也实现了Connection接口。

public class DelegatingConnection<C extends Connection> extends AbandonedTrace
        implements Connection {

        }

现在,我们关注重点在于PoolableConnection的close()方法,如下:

public synchronized void close() throws SQLException {
        //  只有这里才是真正的关闭,原生Connection.close()
        if (isClosedInternal()) {
            return;
        }

        boolean isUnderlyingConectionClosed;
        try {
            //  获取真正的数据库连接对象,判断原生数据库连接是否真的关闭了
            isUnderlyingConectionClosed = getDelegateInternal().isClosed();
        } catch (SQLException e) {
            //  销毁数据库连接池中的无效对象
            try {
                _pool.invalidateObject(this);
            } catch(IllegalStateException ise) {
                // 如果连接池已关闭,则销毁对象
                passivate();
                getInnermostDelegate().close();
            } catch (Exception ie) {
            }
            throw new SQLException("Cannot close connection (isClosed check failed)", e);
        }

        /* 在数据库连接校验完成之前,不能关闭连接
         * 数据库连接已归还到池中的时候,也不能关闭连接
         */
        if (isUnderlyingConectionClosed) {
             try {
                _pool.invalidateObject(this);
            } catch(IllegalStateException e) {
                passivate();
                getInnermostDelegate().close();
            } catch (Exception e) {
                throw new SQLException("Cannot close connection (invalidating pooled object failed)", e);
            }
        } else {
            try {
                _pool.returnObject(this);
            } catch(IllegalStateException e) {
                passivate();
                getInnermostDelegate().close();
            } catch(SQLException e) {
                throw e;
            } catch(RuntimeException e) {
                throw e;
            } catch(Exception e) {
                throw new SQLException("Cannot close connection (return to pool failed)", e);
            }
        }
    }

从上面的代码可以看出,所谓的关闭只是把数据库连接还会给数据库连接池,并没有真正地关闭数据库连接(调用原生数据库连接的关闭方法),大量的逻辑都在判断原生连接是否真的有效(未关闭状态)。

结论

我们所使用的数据库连接执行的关闭,并不是真正意义上的断开数据库连接,而只是做了一个可用性标记(如额外添加一个boolean字段,用于判断状态),从而利于连接池对有效连接的判断。

也因为这样的原因,数据库连接池也有缺点,如在开发过程中,如果代码设置的初始化连接数量过大,开发服务器的真实连接数量可能不够用,从而出现没有可用数据库连接的错误。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值