前提:

当只有一个生产者与消费者,也就是只有两个线程时,唤醒的永远时对方线程。

当只有一个生产者时和两个消费者,或者两个生产者与两个消费者时,唤醒的就是所有线程了,这样就会产生死锁和虚假唤醒。

多线程对多线程,必会有死锁和虚假唤醒问题。

比如下面的代码,初看正常,刚运行几次也正常,但多运行几次就出现问题了。大概率出现的问题是死锁,程序无穷无尽的卡住了。

我们来理解这个问题:C吃完了包子,此时包子为0个,C唤醒了厨师A和吃货B这两个线程。

因为A和B都醒了,A作为厨师去生产包子了。

B作为吃货一看包子为0个,就等着,而且是死等;当厨师A做完包子,唤醒大家去吃包子时,B还在死等,而此时虽然C也被唤醒了。

但C一看被锁住了,而这个吃货B却死等着不让,于是B和C就僵持住了。

相当于:餐厅里只有一个位置,B一看没有包子了,就死等;在B死等的时候,厨师A做好了一个包子并叫大家去吃。

C高兴的去吃包子,但位置被B占用了,而且B不知道厨师做好了包子,一直死等,一直占着位置,这样一来,就卡在B这里了,C和A都急死了。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Bun {
    public static void main(String[] args) {
        SteamedBun bun = new SteamedBun();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    bun.produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }, "厨师A").start();//定义了一个厨师线程,生长包子
        new Thread(() -> {

            for (int i = 0; i < 5; i++) {
                try {
                    bun.eat();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "吃货B").start();//定义了一个吃货线程,吃包子


        new Thread(() -> {

            for (int i = 0; i < 5; i++) {
                try {
                    bun.eat();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "吃货C").start();//定义了一个吃货线程,吃包子

    }
}


class SteamedBun {
    private int num = 0;//包子数量

    //生产者,包子铺的厨师
    public synchronized void produce() throws InterruptedException {
        while (num != 0) {
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "生产了1个包子");
        this.notifyAll();
    }


    //消费者-->吃包子的吃货
    public synchronized void eat() throws InterruptedException {
        while (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "吃了1个包子");
        this.notifyAll();
        System.out.println(Thread.currentThread().getName()+"叫厨师去做包子");
    }
}

以上代码运行结果偶尔正常,偶尔发生本文开头提到的死锁。

下面的结果只有14行,就卡在14行不动了。

厨师A生产了1个包子
吃货B吃了1个包子
吃货B叫厨师去做包子
厨师A生产了1个包子
吃货B吃了1个包子
吃货B叫厨师去做包子
厨师A生产了1个包子
吃货C吃了1个包子
吃货C叫厨师去做包子
厨师A生产了1个包子
吃货B吃了1个包子
吃货B叫厨师去做包子
厨师A生产了1个包子
吃货C吃了1个包子
吃货C叫厨师去做包子