飞道的博客

IllegalMonitorStateException异常 | 生产者消费者模式

317人阅读  评论(0)

结论先行:

这是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可以实现精准通知唤醒。


转载:https://blog.csdn.net/wd_888/article/details/105154184
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场