今天在實現阻塞隊列時,代碼拋出了 java.lang.IllegalMonitorStateException 異常。代碼以下:java
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.this
翻譯過來就是:spa
拋出該異常代表,一個線程試圖在一個對象的監視器上等待,或通知已經在對象的監視器上等待而不擁有指定監視器的其餘線程。線程
咱們再來看 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. code
簡單說就是在調用wait()或者notify()以前,必須使用synchronized語義綁定住被wait/notify的對象。對象
而上述代碼若是給queue加鎖,顯而易見會出現死鎖問題,因此最後我將代碼改爲了下面的形式。解決了問題。blog
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(); } }
後來在網上看到另一段代碼也會拋出相同異常:three
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異常。