今天在实现阻塞队列时,代码抛出了 java.lang.IllegalMonitorStateException 异常。代码如下:
public class BlockingQueue_1 {
private final List<Object> queue = new LinkedList<>();
private int capacity = 10;
public BlockingQueue_1() {
}
public BlockingQueue_1(int capacity) {
this.capacity = capacity;
}
public synchronized Object put(Object item) throws InterruptedException {
while (queue.size() >= capacity) {
wait();
}
queue.add(item);
queue.notifyAll();
return item;
}
public synchronized void remove() throws InterruptedException {
while (0 == queue.size()) {
wait();
}
queue.remove(0);
queue.notifyAll();
}
public synchronized int getSize() {
return queue.size();
}
}
JavaDoc上关于IllegalMonitorStateException 的解释是:
Thrown to indicate that a thread has attempted to wait on an object's monitor or to notify other threads waiting on an object's monitor without owning the specified monitor.
翻译过来就是:
抛出该异常表明,一个线程试图在一个对象的监视器上等待,或通知已经在对象的监视器上等待而不拥有指定监视器的其他线程。
我们再来看 Object.notify() 这个函数的JavaDoc中有相关的解释:
A thread becomes the owner of the object's monitor in one of three ways:
1. By executing a synchronized instance method of that object.
2. By executing the body of a synchronized statement that synchronizes on the object.
3. For objects of type Class, by executing a synchronized static method of that class.
简单说就是在调用wait()或者notify()之前,必须使用synchronized语义绑定住被wait/notify的对象。
而上述代码如果给queue加锁,显而易见会出现死锁问题,所以最后我将代码改成了下面的形式。解决了问题。
public class BlockingQueue_2 {
private final List<Object> queue = new LinkedList<>();
private int capacity = 10;
public BlockingQueue_2() {
}
public BlockingQueue_2(int capacity) {
this.capacity = capacity;
}
public synchronized Object put(Object item) throws InterruptedException {
while (queue.size() >= capacity) {
wait();
}
queue.add(item);
notifyAll();
return item;
}
public synchronized void remove() throws InterruptedException {
while (0 == queue.size()) {
wait();
}
queue.remove(0);
notifyAll();
}
public synchronized int getSize() {
return queue.size();
}
}
后来在网上看到另外一段代码也会抛出相同异常:
private boolean wait = false;
public boolean pleaseWait() {
synchronized (this.wait) {
if (this.wait == true) {
return false;
}
this.wait = true;
try {
this.wait.wait();
} catch (InterruptedException e) {
}
return true;
}
}
这里的问题在于 this.wait 这个变量是一个Boolean,并且在调用this.wait.wait()之前,this.wait执行了一次赋值操作:
this.wait = true;
Boolean型变量在执行赋值语句的时候,其实是创建了一个新的对象。也就是说,在赋值语句的之前和之后,this.wait并不是同一个对象。
synchronzied(this.wait)绑定的是旧的Boolean对象,而this.wait.wait()使用的是新的Boolean对象。由于新的Boolean对象并没有使用synchronzied进行同步,所以系统抛出了IllegalMonitorStateException异常。