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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值