# wait notifyAll 线程间通信

## 生产者 - 消费者 模型

\
假设我们有一个场景:‌

生产者向传送带上放入一件商品，消费者从传送带上取出一件商品。‌

**简单来说，就是生产一件商品，消费一件商品。**

![](https://github.com/chesterzhang/intro_Java/blob/zhc_dev/picture/thread/producer-consumer.png?raw=true)

我们用两个线程来表示 生产者 和 消费者。比如 生产者 看到传送带上 有一件商品，就原地等待。消费者看到传送带 上有商品 就取一件商品，然后传送带上没有商品了，消费者也原地等待。**生产者，消费者互相等待，造成死锁**。

为了解决死锁，很显然 两个线程需要相互通信。我们要对两个 线程 的功能略作修改:&#x20;

* 生产者负责将产品并且放入传送带，一次放一件商品，同时提醒消费者从传送带取商品。当传送带上有商品时， 提醒消费者取出商品，生产者 等待 消费者。
* 消费者负责从传送带上取出商品，一次取一件商品，取出后提醒生产者在传送带上放入商品。

  若传送带上没有商品，提醒生产者在传送带上放入商品，消费者等待 生产者。

## wait() 方法 与 notifyAll() 方法

使线程进入等待状态，可以调用 线程 wait() 函数实现。

唤醒其他线程进入就绪状态, 可以调用notify() 或者 notifyAll() 方法实现。两者的区别是，notify()唤醒哪一个线程，有操作系统决定，notifyAll()一次性唤醒所有等待状态的线程。实际开发中推荐使用 notifyAll() 函数，因为 notify() 函数可能没有唤醒 我们想要唤醒的线程。

为了让两个线程互相知道什么时候应该进入等待状态, 什么时候需要唤醒另外一个线程，我们需要设置一个**全局的互斥量。这个互斥量用于表示传送带上是否有商品，若有商品，生产者等待，唤醒消费者。若无商品，消费者等待，唤醒生产者。**

我们编写一个 GoodsPipe 类， 用来模拟商品传送带。 GoodsPipe 有两个函数 send 和 pick, 分别表示向传送带上分入一个商品，和从传送带上取下一件商品。我们还设置了 一个变量 n 用于表示传送带上商品数量，一个布尔型变量 hasGood 用于表示传送带上是否还有商品。我们对 send 函数 和 pick 函数 都用 synchronization 进行修饰，表示任意线程调用这个方法，其他线程都不允许抢占。

```java
public class GoodsPipe {

    private int n;//商品数量
    boolean hasGood;

    public GoodsPipe(){
        n=0;
        hasGood=false;
    }

    //向传送带上放一个商品
    //我们希望放入产品时,不会中途取出一个产品,导致产品数量出错
    public synchronized void send()  {
        if (hasGood){//当传送带上有商品时,不需要生产,等待消费
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            n++;
            hasGood=true;
            System.out.println("生产 一个商品,还剩"+n+"个商品");
            notifyAll();//唤醒消费者
        }
    }

    //从传送带上去一个商品
    //我们希望取出产品时,不会中途放入一个产品,导致产品数量出错
    public synchronized void pick() {
        if (! hasGood){//如果没有商品,无法消费,等待生产
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            n--;
            hasGood=false;
            System.out.println("消费 一个商品,还剩"+n+"个商品");
            notifyAll();//唤醒生产者
        }

    }

}
```

我们编写了Produce 类， 用于传递给 Thread 构造函数 创建 生产者 线程。

```java
//用于创建 生产者 线程
public class Producer implements  Runnable{
    private GoodsPipe goodsPipe;

    public Producer(GoodsPipe goodsPipe) {
        this.goodsPipe = goodsPipe;
    }

    //模拟生产商品
    @Override
    public void  run() {
        //不停地向传送带上送商品
        while(true){
            goodsPipe.send();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
```

我们还编写了 Consumer 类， 用于传递给 Thread 构造函数 创建 消费者 线程。

```java
public class Consumer implements Runnable{
    private GoodsPipe goodsPipe;

    public Consumer(GoodsPipe goodsPipe) {
        this.goodsPipe = goodsPipe;
    }

    //模拟消费产品
    @Override
    public void run() {
        while (true){
            goodsPipe.pick();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
```

最后我们还编写了一个测试类，用于测试 生产者 - 消费者 模型。

```java
public class Test {

    public static void main(String[] args) {
        GoodsPipe pipe = new GoodsPipe();

        //生产者, 消费者 线程
        Thread p=new Thread(new Producer(pipe));
        Thread c=new Thread(new Consumer(pipe));

        //运行
        p.start();
        c.start();

    }

}
```

部分运行结果：

```
生产 一个商品,还剩1个商品
消费 一个商品,还剩0个商品
生产 一个商品,还剩1个商品
消费 一个商品,还剩0个商品
生产 一个商品,还剩1个商品
消费 一个商品,还剩0个商品
生产 一个商品,还剩1个商品
消费 一个商品,还剩0个商品
生产 一个商品,还剩1个商品
消费 一个商品,还剩0个商品
生产 一个商品,还剩1个商品
消费 一个商品,还剩0个商品
```

最后输出结果，完全如我们所预积的那样，生产一件商品，消费一件商品，这说明 生产者 线程与 消费者 线程  建立了通信。**这个通信本质上时通过一个 全局互斥变量 hasGoods 实现的。**
