IllegalMonitorStateException異常 | 生產者消費者模式

結論先行:

在這裏插入圖片描述這是JDK對這異常的定義。就是說線程沒有拿到對應對象的監視器,也就不能在監視器上完成wait或者notify等操作。
解決辦法:
加上synchronized,線程就能拿到對象的監視器了。我的理解就是通過synchronized讓線程拿到了對象鎖,鎖定了這個對象,那這個對象的監視器我耍耍問題不大吧。(手動狗頭)

另:下面會繼續講解出錯的整個過程,以及生產者消費者模式的兩種實現方式。


生產者消費者-synchronized版

出錯版本:

public class Producter {
    public static void main(String[] args) {
        PandA pandA = new PandA();

        new Thread(()->{
            pandA.increment();
        },"A").start();

        new Thread(()->{
            pandA.increment();
        },"B").start();
    }

}


class PandA{
    private int num = 0;

    public void increment(){
        if (num != 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        num++;
        this.notify();
        System.out.println(Thread.currentThread().getName()+"生產了一個漢堡");

    }

    public void decrement(){
        if (num == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        num--;
        this.notify();
        System.out.println(Thread.currentThread().getName()+"消費了一個漢堡");

    }

}

運行報錯的異常就是IllegalMonitorStateException。原因就是線程沒有拿到對象的監視器。所以可以簡單記憶,調用wait時請搭配上synchronized。

正確版本:

public class Producter {
    public static void main(String[] args) {
        PandA pandA = new PandA();

        new Thread(()->{
            for (int i=0;i<15;i++){
                pandA.increment();
            }

        },"A").start();

        new Thread(()->{
            for (int i=0;i<15;i++){
                pandA.decrement();
            }

        },"B").start();
    }

}


class PandA{
    private int num = 0;

    public synchronized void increment(){
        while (num != 0){
            try {
                //這個this就是這個對象,不是這個線程,我理解錯了。wait(),notify()都是繼承自Object的方法
                //this.wait()讓當前對象的調用者線程休眠去了
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        num++;
        this.notify();
        System.out.println(Thread.currentThread().getName()+"生產了一個漢堡"+num);

    }

    public synchronized void decrement(){
        while (num == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        num--;
        this.notify();
        System.out.println(Thread.currentThread().getName()+"消費了一個漢堡"+num);

    }

}
生產者消費者-Lock版

同樣先給個錯誤版本:

public class ProducterLock {
    public static void main(String[] args) {
        test t = new test();
        new Thread(()->{
            for (int i=0;i<15;i++){
                t.increments();
            }
        },"A").start();

        new Thread(()->{
            for (int i=0;i<15;i++){
                t.decrements();
            }
        },"B").start();

    }
}

class test{
    private int num = 0;
    private Lock lock = new ReentrantLock();

    public void increments(){
        lock.lock();
        while(num != 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"生產了一個漢堡"+num);
        lock.unlock();
    }

    public void decrements(){
        {
            lock.lock();
            while(num == 0){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"消費了一個漢堡"+num);
            lock.unlock();
        }

    }
}

**運行結果:**同樣會報IllegalMonitorStateException異常,但這次就有疑問了,我們加了Lock鎖啊,沒有鎖住對象?繼續去翻看JDK,找到ReentrantLock,看一下方法會發現一個方法:
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

Condition的定義,以及Condition提供的方法。我的疑問也到了解釋,監視器方法wait等根本不適合Lock,和Lcok配套的是Condition,他們取代了synchronized和監視器方法的一整套實現方案。

正確代碼:

public class ProducterLock {
    public static void main(String[] args) {
        test t = new test();
        new Thread(()->{
            for (int i=0;i<15;i++){
                t.increments();
            }
        },"A").start();

        new Thread(()->{
            for (int i=0;i<15;i++){
                t.decrements();
            }
        },"B").start();

    }
}

class test{
    private int num = 0;
    private Lock lock = new ReentrantLock();

    Condition condition = lock.newCondition();

    public void increments(){
        lock.lock();
        while(num != 0){
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num++;
        condition.signal();
        System.out.println(Thread.currentThread().getName()+"生產了一個漢堡"+num);
        lock.unlock();
    }

    public void decrements(){
        {
            lock.lock();
            while(num == 0){
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            num--;
            condition.signal();
            System.out.println(Thread.currentThread().getName()+"消費了一個漢堡"+num);
            lock.unlock();
        }
    }
}

上面代碼正確的輸出結果如圖:
在這裏插入圖片描述
對了,兩種方法Lock版本會好一點,因爲Condition可以實現精準通知喚醒。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章