结论在前
ReentrantLock的锁对象,在释放锁
之前,必须确保已上锁(锁的state=1)
。在未上锁状态下,不能释放锁
,若强行释放锁,将报IllegalMonitorStateException异常。
未上锁状态:包括主动不上锁或其他异常导致上锁不成功
措施:为避免次异常,养成良好的编程习惯,将上锁代码放在所有可能异常代码之前,或者简单粗暴的将上锁代码放在方法的第一行!
项目场景:
某金融系统在运行时,通过查看日志,发现报了一个IllegalMonitorStateException运行时异常
问题描述
在执行某方法时,先后连续报了java.lang.NullPointerException
和java.lang.IllegalMonitorStateException
异常。经检查,报错的方法逻辑朴实无华,唯一就是用到了一个ReentrantLock。
异常代码类似如下:
private static Logger logger = LoggerFactory.getLogger(TestService.class);
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
try {
String name = null;
String[] arr = name.split(","); // 1、此处将触发空指针异常
lock.lock();
} catch (Exception e) {
logger.error("ERROR : ", e); // 2、打印异常
} finally {
lock.unlock(); // 3、释放锁
}
System.out.println("【 excute successfully 】");
}
java.lang.IllegalMonitorStateException异常的API解释:
线程尝试等待一个对象的监视器或者去通知其他正在等待这个对象监视器的线程时,但是没有拥有这个监视器的所有权,
就会抛出这个异常
debug截图:
注:由于空指针导致未成功上锁(锁的状态是0),catch完异常后,直接执行finally代码块中的释放锁,报异常(未上锁,不能释放锁,不然异常)
运行控制台截图:
原因分析:
提示:针对ReentrantLock的对象lock,如果上锁,和释放锁不是在同一个线程中完成,就会抛出
IllegalMonitorStateException
异常
程序运行到29行时,由于空指针异常,导致lock未上锁(30行的上锁未执行,此时锁的状态是0),最后执行finally代码块中的释放锁,导致报错。
注:未上锁的情况下,直接释放锁会触发``IllegalMonitorStateException``异常
解决方案:
修改:在finally代码块释放锁之前,确保成功上锁了。即上锁的代码需要放在可能有异常的代码之前。最简单粗暴的方法就是在方法第一行加锁。这样保证了释放锁的时候,锁肯定了上锁状态(锁的状态为1)
修改代码:将上锁逻辑提前
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
try {
String name = null;
lock.lock(); // 在可能抛其他异常前上锁,避免因未上锁状态下释放锁而导致IllegalMonitorStateException异常。上锁后,lock状态是1
String[] arr = name.split(","); // 1、此处将触发空指针异常。但是已成功上锁
} catch (Exception e) {
logger.error("ERROR : ", e); // 2、打印异常
} finally {
lock.unlock(); // 3、释放锁。此时lock锁可以正常释放,不会报IllegalMonitorStateException异常
}
System.out.println("【 excute successfully 】");
}
运行完控制台:程序未报运行时异常,成功执行结束