刚刚开通了一个公众号,会分享一些技术博客和自己觉得比较好的项目,同时会更新一些自己使用的工具和图书资料,后面会整理一些面试资料进行分享,觉得有兴趣的可以关注一下。
背景
今天在修复sonarqube
的时候,碰到一个需要将Thread.sleep
修复为wait
方法,然后碰到这么个报错。
java.lang.IllegalMonitorStateException: current thread is not owner
at java.base/java.lang.Object.wait(Native Method)
代码
代码比较简单
private final static Object lock = new Object();
private final static Random rand = new SecureRandom();
public synchronized static String genNextKey() {
long currentTimeMillis = System.currentTimeMillis();
int count = 0;
while (currentTimeMillis <= lastTimeMill) {
try {
lock.wait((10L + rand.nextInt(90)));
} catch (InterruptedException e) {
log.warn("IdGenerator time interrupt", e);
Thread.currentThread().interrupt();
}
currentTimeMillis = System.currentTimeMillis();
count ++;
if (count >= MAX_COUNT ) {
throw new RuntimeException("get ID failed");
}
}
lastTimeMill = currentTimeMillis;
return String.valueOf(currentTimeMillis).substring(1, 13);
}
分析
《java编程思想》第四版一书中有写:线程操作的wait()、notify()、notifyAll()方法只能在同步控制方法或同步控制块内调用。如果在非同步控制方法或控制块里调用,程序能通过编译,但运行的时候,将得到 IllegalMonitorStateException 异常,并伴随着一些含糊信息,比如 ‘当前线程不是拥有者’。其实异常的含义是 调用wait()、notify()、notifyAll()的任务在调用这些方法前必须 ‘拥有’(获取)对象的锁。
那明显其实synchronized
和wait
拿的锁就不是一个锁了,只需要将方法上的synchronized
改为代码块即可。
private final static Object lock = new Object();
private final static Random rand = new SecureRandom();
public static String genNextKey() {
synchronized (lock) {
long currentTimeMillis = System.currentTimeMillis();
int count = 0;
while (currentTimeMillis <= lastTimeMill) {
try {
lock.wait((10L + rand.nextInt(90)));
} catch (InterruptedException e) {
log.warn("IdGenerator time interrupt", e);
Thread.currentThread().interrupt();
}
currentTimeMillis = System.currentTimeMillis();
count ++;
if (count >= MAX_COUNT ) {
throw new RuntimeException("get ID failed");
}
}
lastTimeMill = currentTimeMillis;
return String.valueOf(currentTimeMillis).substring(1, 13);
}
}
其实是很基本的一个问题,技术基本功不扎实,还需要努力!