Apache Commons DBCP2连接池出现超时错误(Cannot get a connection, pool error Timeout waiting for idle object)的修复求助
看起来你遇到了连接池资源耗尽的棘手问题,明明调用了con.close()想要归还连接,但循环到第9次之后还是触发了超时错误,咱们一步步来排查和修复:
1. 致命问题:每次获取连接都新建了一个连接池实例
看你的getConnection()方法,每次调用都会执行new BasicDataSource()——这意味着每个请求都在创建一个全新的连接池,而不是复用同一个全局池!每个新池的maxTotal=8,但这些池之间的连接完全不共享,旧池的连接会因为没有引用被慢慢回收,但回收速度远远赶不上你循环创建新池的速度,很快就会耗尽数据库的总连接数,导致后续请求超时。
修复方案:把BasicDataSource改成单例,全局只初始化一次
// 全局单例的连接池实例 private static BasicDataSource dataSource; // 静态代码块只在类加载时执行一次,初始化连接池 static { dataSource = new BasicDataSource(); dataSource.setDriverClassName(JDBC_DRIVER); dataSource.setUrl(db.url); dataSource.setUsername(db.username); dataSource.setPassword(db.password); dataSource.setInitialSize(2); dataSource.setMaxTotal(8); dataSource.setLogAbandoned(true); dataSource.setMinIdle(2); dataSource.setMaxWaitMillis(10000); dataSource.setTestOnReturn(true); dataSource.setValidationQuery("SELECT 1"); // 这个值太小了,会导致回收线程疯狂运行,建议改成30秒(30000毫秒) dataSource.setTimeBetweenEvictionRunsMillis(30000); } public Connection getConnection(){ return dataSource.getConnection(); }
2. 资源未正确关闭,导致连接无法归还
你的select_sql方法里只关闭了Connection,但PreparedStatement和ResultSet完全没处理。这些资源如果不关闭,连接池会认为当前连接还在被占用,无法正确归还到池中;而且close()方法可能抛出异常,导致后续的关闭操作直接跳过,进一步加剧资源泄漏。
修复方案:在finally块中按逆序关闭所有资源,每个操作单独捕获异常
public static JSONArray select_sql(String sql){ Connection con = null; ResultSet rs = null; PreparedStatement ps = null; try { con = DataBasePool.getConnection(systemName); ps = con.prepareStatement(sql); // 注意:这里不要传sql参数,ps已经预编译过了,传参会重新创建语句,浪费资源 rs = ps.executeQuery(); ResultSetMetaData metaData = rs.getMetaData(); while (rs.next()){ // 你的业务逻辑 } // 返回处理后的JSONArray } catch (Exception e) { // 注意拼写:是printStackTrace,不是printStrace e.printStackTrace(); } finally { // 按ResultSet → PreparedStatement → Connection的顺序关闭(创建顺序的逆序) if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con != null) { try { con.close(); // 这里才会真正把连接归还到池里 } catch (SQLException e) { e.printStackTrace(); } } } // 返回默认结果或处理后的数组 }
3. 连接池参数优化
你设置的timeBetweenEvictionRunsMillis=2毫秒,这会导致连接池的空闲连接回收线程每秒运行500次,占用大量CPU资源,严重影响池的正常运作。建议改成30000毫秒(30秒),或者根据你的业务场景调整为合理的间隔。
最后总结
先修复连接池单例的问题(这是核心原因),再确保所有数据库资源都被正确关闭,最后优化参数。这样循环15次的时候,连接会被正确归还并复用,就不会再出现超时错误了。
备注:内容来源于stack exchange,提问作者woxiangbo




