关于生产者消费者在使用if条件时出错

113 阅读2分钟

关于生产者消费者在使用if条件时出错

今天学习了线程,写了个生产者消费者问题的代码如下

Test类

package com.xc.waitdemo3;

public class Test {

    public static void main(String[] args) {
        //创建公共区
        Clerk clerk = new Clerk();
        //创建线程
        ProduceThread produceThread = new ProduceThread(clerk);
        ConsumeThread consumeThread = new ConsumeThread(clerk);
        

       //线程启动
        produceThread.start();
        consumeThread.start();
        
    }
}

生产者

package com.xc.waitdemo3;

public class ProduceThread extends Thread {

    Clerk clerk;

    ProduceThread(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while(true){
            clerk.produce();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

消费者

package com.xc.waitdemo3;

public class ConsumeThread extends Thread {

    Clerk clerk;

    ConsumeThread(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while(true){
            clerk.consume();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

共享区

package com.xc.waitdemo3;

public class Clerk {

    int product = 0;

    /**
     * 生产方法
     */
    public synchronized void produce(){

        if(product==20){

                try {
                    //
                    System.out.println("等待消费。。。");
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

        }
        System.out.println("生产者生产了一个商品....."+product);
        product++;
        this.notify();
    }

    /**
     * 消费方法
     */
    public synchronized void consume(){

        if(product<=0){
            //
            System.out.println("等待生产....");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName()+" 消费者消费了一个产品:"+product);
        product--;

        notify();

    }
}

此时我们消费者生产者调用的方法里面是使用if判断语句进行判断(分别在共享区的第12行和33行),此时我们运行代码,发现代码并没有问题,但是我们一旦多创建几个消费者问题就出现了

package com.xc.waitdemo3;

public class Test {

    public static void main(String[] args) {

        Clerk clerk = new Clerk();
        //多创建几个生产者消费者
       
ProduceThread produceThread = new ProduceThread(clerk);
ConsumeThread consumeThread = new ConsumeThread(clerk);
ConsumeThread consumeThread2 = new ConsumeThread(clerk);
ConsumeThread consumeThread3 = new ConsumeThread(clerk);

produceThread.start();

consumeThread.start();
consumeThread2.start();
consumeThread3.start();
}
}

此时控制台出现了负数

image.png

很神奇,为什么一个消费者不会出现负数呢,于是我仔细研究了一下代码 发现问题就出现在if这里,下面来讲一下线程流程

如果库存已经没有产品了,此时消费者还想着来消费,就会被经过if条件判断,判断已经没有产品之后就会直接wait

image.png 假设来了四个消费者,经过判断全部被wait,此时生产者启动

image.png

当生产者生产好了一个产品,notify()一个消费者,这个被唤醒的消费者直接将产品消费掉,然后再notify其他被wait的线程,此时其他被wait的线程只剩下了三个之前被wait的消费者,但是此时被随机唤醒一个,唤醒之后直接进入下一步

image.png

已经为零的产品此时继续减减,然后就出现了负数,继续唤醒剩下两个消费者,于是继续减减,负的越来越多,而生产者根本忙不过来,此时就出现了负数。

而将if换成while时就会出现错误,是为什么呢?

是因为while()语句没有达成内部条件就不能出去,被唤醒的消费者再产品小于0时会被一直wait,知道有库存就可以出while循环此时就可以杜绝出现负数的情况。