多线程Java应用调用Hibernate保存数据时线程阻塞问题排查
问题描述
我正在开发一个个人项目,采用多线程调用DAO的save方法进行数据保存。第一个线程执行该方法一切正常,但第二个线程在执行save方法的增强for循环时出现停滞,无法继续执行。不确定需要修改Java代码、Hibernate配置,还是调整MariaDB的设置(注:每次保存的registers列表包含约100条数据,不清楚数据量是否与此问题相关)。
DAO中的save方法
public synchronized void save(List<Registers> registers) throws Exception { Session session = null; Transaction transaction = null; try { session = HibernateUtil.getSessionFactory(this.url).openSession(); transaction = session.beginTransaction(); for (final Registers register : registers) { session.save(register); } transaction.commit(); } catch (final Exception e) { if (transaction != null) { transaction.rollback(); } throw e; } finally { if (session != null && session.isOpen()) { session.close(); } } }
Registers实体类
@SuppressWarnings("serial") @Data @NoArgsConstructor @AllArgsConstructor @ToString @Entity @Table(name = "TAB_REGISTERS") public class Registers implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "COD_REG") private Long cod; // 其余映射变量的@Column注解及构造方法 }
Hibernate配置文件hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="javax.persistence.jdbc.driver">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">false</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.order_updates">true</property> <property name="hibernate.enable_lazy_load_no_trans">true</property> <property name="hibernate.c3p0.min_size">1</property> <property name="hibernate.c3p0.max_size">2</property> <property name="hibernate.c3p0.timeout">100</property> <property name="hibernate.c3p0.max_statements">1000</property> <property name="hibernate.c3p0.idle_test_period">100</property> </session-factory> </hibernate-configuration>
DAO调用代码
public void saveNewRegisters(List<Registers> registers){ try { this.dao.save(registers); } catch (final Exception e) { logger.error("save()", e); } }
问题排查与解决建议
1. 连接池配置优化
当前c3p0连接池max_size=2的设置仅允许同时存在2个数据库连接,在多线程场景下极易出现连接等待。建议调整参数扩大连接池:
<property name="hibernate.c3p0.min_size">5</property> <property name="hibernate.c3p0.max_size">10</property> <property name="hibernate.c3p0.timeout">300</property> <!-- 延长超时时间,避免短时间内连接被回收 -->
2. 移除不必要的synchronized关键字
当前save方法添加的synchronized会强制多线程串行执行,但方法内部每次都会新建独立的Session,无需通过方法同步保证线程安全。移除该关键字后,多线程可并行执行,配合连接池优化提升效率。
3. 优化批量插入逻辑
循环调用session.save()会产生大量单条插入语句,不仅效率低下,还可能引发数据库锁竞争。建议改用批量插入方式:
- 在Hibernate配置中添加批量插入相关参数:
<property name="hibernate.jdbc.batch_size">50</property> <!-- 每50条数据批量提交 --> <property name="hibernate.order_inserts">true</property> <!-- 按表排序插入,减少锁竞争 --> - 修改save方法中的插入逻辑:
// 替换原循环save逻辑 for (int i = 0; i < registers.size(); i++) { session.save(registers.get(i)); if (i % 50 == 0) { // 每50条刷新一次session,避免内存溢出 session.flush(); session.clear(); } }
4. 检查SessionFactory的单例实现
确保HibernateUtil.getSessionFactory(this.url)是单例模式,避免每次调用都新建SessionFactory导致连接池资源分散。如果每个SessionFactory实例占用独立连接池,极易出现连接耗尽的情况。
5. 数据库锁排查
使用MariaDB命令查看当前进程状态,确认是否存在锁等待:
SHOW PROCESSLIST;
若发现第二个线程对应的SQL处于锁等待状态,需排查是否有其他事务长时间占用表锁,或调整数据库的锁超时参数。
6. 更换适配MariaDB的驱动与方言
当前配置使用MySQL的驱动和方言,建议替换为MariaDB专用版本提升兼容性:
<property name="javax.persistence.jdbc.driver">org.mariadb.jdbc.Driver</property> <property name="hibernate.connection.driver_class">org.mariadb.jdbc.Driver</property> <property name="hibernate.dialect">org.hibernate.dialect.MariaDBDialect</property>
内容的提问来源于stack exchange,提问作者Luiz Fernando da Silva Santos

