noear / wood

noear::微型ORM框架(支持:java sql,xml sql,annotation sql;事务;缓存;监控;等...)
Apache License 2.0
13 stars 3 forks source link

DbContextMetaData中的锁使用导致IllegalMonitorStateException #6

Closed balajinima closed 1 week ago

balajinima commented 1 week ago

问题描述

在DbContextMetaData类的init()方法中,我们遇到了java.lang.IllegalMonitorStateException异常。这个异常发生在尝试释放一个未被当前线程持有的锁时。 具体的异常堆栈如下:

java.lang.IllegalMonitorStateException
    at java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:175)
    at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1007)
    at java.base/java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:494)
    at org.noear.wood.DbContextMetaData.init(DbContextMetaData.java:215)
    at org.noear.wood.DbContextMetaData.getDialect(DbContextMetaData.java:149)
    at org.noear.wood.DbContext.getDialect(DbContext.java:96)
    at org.noear.wood.Command.fullText(Command.java:145)
    at org.noear.wood.SQLer.buildCMD0(SQLer.java:409)
    at org.noear.wood.SQLer.buildCMD(SQLer.java:366)
    at org.noear.wood.SQLer.execute(SQLer.java:245)
    at org.noear.wood.DbAccess.execute(DbAccess.java:126)
    at org.noear.wood.DbContext.exe(DbContext.java:437) 

问题原因

问题出现在init()方法中对ReentrantLock的使用上。当前的实现如下:

public boolean init() {
    if (dialect != null) {
        return true;
    }

    SYNC_LOCK.tryLock();
    try {
        if (dialect != null) {
            return true;
        }

        return initDo();
    } finally {
        SYNC_LOCK.unlock();
    }
}

这段代码在以下情况下会产生问题:

多个线程同时调用init()方法。 第一个线程成功获取锁并执行。 第二个线程调用tryLock()失败(返回false),但仍然执行到finally块。 第二个线程尝试unlock(),但它并未持有锁,因此抛出IllegalMonitorStateException。

问题的根源在于tryLock()方法不保证一定能获取到锁,但当前的代码结构假设了总是能获取到锁。

解决方案

为了解决这个问题,我们需要修改init()方法,确保只有在成功获取锁的情况下才执行解锁操作。以下是修改后的代码:

public boolean init() {
    if (dialect != null) {
        return true;
    }

    boolean locked = SYNC_LOCK.tryLock();
    try {
        if (locked) {
            if (dialect != null) {
                return true;
            }
            return initDo();
        }
        return false;
    } finally {
        if (locked) {
            SYNC_LOCK.unlock();
        }
    }
}

这个修改确保只有在成功获取锁的情况下才会尝试释放锁,从而避免了IllegalMonitorStateException。

影响

这个问题可能导致在高并发情况下出现异常,影响系统的稳定性。修复这个问题将提高代码的健壮性和并发安全性。

相关代码

文件:DbContextMetaData.java

代码版本

1.3.10

通用建议

检查整个类中所有使用 SYNC_LOCK 的地方,如refresh(),initTables()等。 确保在使用 tryLock() 时总是检查其返回值。

noear commented 1 week ago

谢谢反馈。。。修复好了;;;已发布:1.3.12