多线程直接new Connection会导致连接数暴涨、端口耗尽、数据库拒绝连接;根本原因是连接是重量级资源且数据库有最大连接数限制;必须用连接池管理并正确关闭。

多线程直接 new Connection 会出什么问题
Java 多线程环境下,如果每个线程都调用 DriverManager.getConnection() 创建新连接,会出现连接数暴涨、TCP 端口耗尽、数据库拒绝连接(java.sql.SQLNonTransientConnectionException: Could not create connection)等问题。根本原因是数据库连接是重量级资源,创建/销毁开销大,且数据库服务端有最大连接数限制(如 MySQL 默认 max_connections=151)。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 禁止在多线程中手动
new Connection或反复调用getConnection() - 必须使用连接池(如 HikariCP、Druid、Apache DBCP2),由池统一管理生命周期
- 连接获取后务必在
try-with-resources或finally块中调用connection.close()—— 这里 close 实际是归还给池,不是真关闭
HikariCP 并发参数怎么设才不拖垮数据库
HikariCP 的核心并发控制参数只有两个:最大连接数 maximumPoolSize 和最小空闲连接数 minimumIdle。设得太大,数据库扛不住;设得太小,线程阻塞排队,吞吐上不去。关键不是拍脑袋,而是结合业务真实负载定。
实操建议:
立即学习“Java免费学习笔记(深入)”;
-
maximumPoolSize初始值可设为数据库max_connections * 0.8(留余量),再根据压测时的pool usage指标动态调优 -
minimumIdle建议与maximumPoolSize相同(即常驻连接),避免高并发时频繁创建连接;若连接空闲成本高(如跨机房),可设为maximumPoolSize / 2 - 务必开启
leakDetectionThreshold(如 60000ms),防止 Connection 忘记 close 导致连接泄漏 - 避免设置
connectionTimeout过长(默认 30s),否则线程会长时间卡在获取连接上
事务边界和 Connection 绑定怎么不出错
多线程下,一个 Connection 不能跨线程复用,而 Spring 的 @Transactional 默认基于 ThreadLocal 绑定当前线程的 Connection。如果手动起新线程(如 CompletableFuture.supplyAsync()),事务就失效了,且可能拿到不同连接,造成数据不一致或死锁。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 不要在
@Transactional方法内直接 new Thread 或用ForkJoinPool异步执行 DB 操作 - 如需异步写库,应将必要参数传入新线程,并在新线程内显式获取新 Connection(通过
DataSource.getConnection()),并自行管理事务(conn.setAutoCommit(false)+commit()/rollback()) - 若用 Spring,可配合
TransactionTemplate在新线程中显式传播事务逻辑,但 Connection 仍是独立的 - 注意:JDBC 规范明确要求 Connection 不是线程安全的,任何共享行为都属未定义行为
批量插入时 PreparedStatement + addBatch 为什么还是慢
即使用了连接池和预编译语句,单条 executeUpdate() 循环插入 1000 行仍很慢。瓶颈往往不在连接获取,而在网络往返和事务日志刷盘。HikariCP 本身不优化 SQL 执行逻辑,得靠 JDBC 层配置和批处理策略。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 必须启用 JDBC 批处理:
connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)后,循环调用ps.addBatch(),最后ps.executeBatch() - MySQL 需在连接 URL 加参数:
rewriteBatchedStatements=true(否则 JDBC 仍会拆成单条发送) - PostgreSQL 需设
reWriteBatchInserts=true,且注意驱动版本(42.2.0+) - 每批大小建议 500–2000 条,过大易 OOM,过小则批处理收益低;可通过
ps.setFetchSize()配合游标优化大结果集读取
连接池不是“开了就行”的开关,maximumPoolSize 和实际 SQL 执行模式(查多还是写多、是否长事务、是否有大字段)强相关。很多线上慢查询,根源是连接池配得看似合理,但某条慢 SQL 占着连接几十秒,把整个池堵死了。监控 HikariPool-1 - Pool stats (total=20, active=20, idle=0, waiting=17) 这类指标,比调参更重要。










