Java併發編程 - java.lang.IllegalMonitorStateException

今天在實現阻塞隊列時,代碼拋出了 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異常。

相關文章
相關標籤/搜索