使用wait/notify同步共享EntityManager触发IllegalMonitorStateException问题排查
EntityManager调用notify()抛出IllegalMonitorStateException的原因及修复
问题场景
我需要检查当前事务是否未处于活跃状态,以执行新事务,因此对共享的EntityManager采用wait()和notify()实现同步,代码如下:
public static void getTransaction() throws Throwable { EntityManager em = getEntityManager(); synchronized (em) { if (em.getTransaction().isActive()) { em.wait(); } } return executeTransaction(); } public static void executeTransaction(){ EntityManager em = getEntityManager(); synchronized (em) { EntityTransaction tx = em.getTransaction(); tx.begin; //execute persist operations tx.commit(); em.notify(); } }
运行时调用em.notify()抛出如下错误:
Caused by: java.lang.IllegalMonitorStateException: null
at java.lang.Object.notify(Native Method)
问题原因
1. 同步对象不一致(核心原因)
IllegalMonitorStateException触发的条件是调用notify()/wait()的线程没有持有该对象的监视器锁。这里最可能的情况是getEntityManager()每次返回的不是同一个EntityManager实例:
- 在
getTransaction()中获取的EntityManager实例,和executeTransaction()中获取的实例不是同一个对象 - 两个方法分别对不同的对象加锁,此时
executeTransaction()中调用notify()的对象,并不是之前getTransaction()中调用wait()的对象,线程虽然持有当前em的锁,但逻辑上完全不匹配,最终触发异常。
2. wait()判断逻辑存在虚假唤醒风险
即使getEntityManager()返回单例,用if判断事务状态也不安全——Java的wait()可能被虚假唤醒(无通知情况下恢复执行),这会导致事务仍活跃时就进入新事务,引发并发问题。
3. 语法错误
代码中tx.begin缺少括号,正确写法是tx.begin();,这会导致编译失败。
修复方案
- 保证同步对象一致:确保
getEntityManager()返回同一个EntityManager单例实例,让wait()和notify()操作的是同一个对象的监视器锁。 - 修正wait()判断逻辑:用
while循环替代if,持续检查事务状态,避免虚假唤醒:synchronized (em) { while (em.getTransaction().isActive()) { em.wait(); } } - 修复语法错误:将
tx.begin改为tx.begin();。
内容的提问来源于stack exchange,提问作者Jonio
相关产品推荐
相关产品推荐

