不能让多个线程共用一个connection实例,因其线程不安全:事务控制、preparedstatement绑定等状态共享会导致数据错乱、异常或串行化执行;正确做法是连接池按需分配或threadlocal线程内复用,且事务必须在同一线程同连接中完成。

Java 中 JDBC 处理多线程并发操作同一连接的问题,核心原则是:**不能让多个线程共用一个 Connection 实例**。因为 JDBC 规范明确要求 Connection 是线程不安全的——多数数据库驱动(如 MySQL Connector/J)在内部维护状态(如事务上下文、网络缓冲区、预编译语句缓存等),多线程复用会导致状态混乱、数据错乱、连接挂死甚至抛出 SQLException。
为什么共享 Connection 会出问题
Connection 不是“无状态工具”,而是有生命周期和会话状态的对象:
- 事务控制(setAutoCommit()、commit()、rollback())是连接级别的,线程 A 调用 commit() 可能提前结束线程 B 正在进行的事务;
- 预编译语句(PreparedStatement)通常绑定在 Connection 上,线程间混用可能触发“Statement closed”或“Invalid operation”异常;
- 部分驱动对并发调用 execute() 会加锁或串行化,表面多线程,实际变成排队执行,失去并发意义。
正确做法:连接不共享,按需获取
每个线程应独立获取自己的 Connection 实例,而不是复用全局静态变量。主流方案有两种:
-
使用连接池(推荐):如 HikariCP、Druid。连接池负责创建、复用、回收 Connection,线程通过
dataSource.getConnection()获取专属连接,用完必须close()(实际归还到池中)。这样既避免频繁建连开销,又天然隔离线程资源。 - ThreadLocal 封装(适合单请求多操作场景):在 Web 应用中,一个 HTTP 请求对应一个线程,可用 ThreadLocal 绑定该线程唯一的 Connection,确保同一线程内多次 DAO 调用复用同一连接(保障事务一致性),但不同线程之间完全隔离。
事务与多线程的配合要点
多线程本身不支持跨线程事务(JDBC 事务无法跨 Connection 传播),所以:
- 若需要多个操作原子性,必须在同一个线程 + 同一个 Connection中完成(开启事务、执行 SQL、统一提交/回滚);
- 不要试图让线程 A 开启事务、线程 B 提交它——这违反 JDBC 模型,也违背 ACID 隔离性;
- 真正需要分布式事务时,应升级到 JTA 或 Saga 模式,而非在 JDBC 层硬凑。
代码层面要避开的坑
以下写法是典型错误:
- 声明
public static Connection conn;并在所有 DAO 中直接使用; - DAO 方法签名带
Connection conn参数,但上层传入的是全局单例; - 用完 Connection 后忘记
close(),导致连接泄漏、池耗尽; - 在 ExecutorService 提交的任务中,未在任务内部获取新连接,而是复用主线程的 Connection。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











