线程间使用wait/notify方式实现的生产消费模型

391 阅读3分钟

都说线程间的通信是使用wait和notify或notifyAll进行通信。这点不用怀疑,大家都很清楚,可是用代码实现怎么简单的处理?下面我举个简单的生产者消费者模型,虽然可以用队列,但是此处只是为了说明线程间的wait和notify原理。

1.首先定定义一个产品

/**
 * 商品
 * 模拟线程间的通信
 * 对象的锁:
 * 锁池(synchronized) 
 * 等待池(wait notify notifyAll)
 *
 * @author houxiurong
 * @date 2019-10-05
 */
public class Product {
    private String brand;
    private String name;
    //..... 省略 getter/setter

    //信号灯的功能 如果有商品 消费者才消费 否则生产者生产
    //默认没有商品 要生产者生成
    private boolean flag = false;

    public synchronized void setProduct(String brand, String name) {
        //flag==true
        //如果有商品,则生产者线程等待一会
        if (flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        this.setBrand(brand);
        try {
            Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setName(name);

        //生产结束 信号标记为true
        System.out.println("生产者生产了" + this.getBrand() + "----" + this.getName());
        flag = true;
        //有商品了,通知其他线程开始作业
        notify();
    }

    public synchronized void getProduct() {
        //如果没有商品 则消费者线程等待
        //flag==false
        if (!flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //有商品,开始消费
        try {
            Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("消费者消费了" + this.getBrand() + "==" + this.getName());

        //消费结束 通知消费者
        flag = false;
        notify();
    }
}

2.定义一个生产者-实现Runnable方法使其具有多线程能力

/**
 * 生产者生产商品
 *
 * @author houxiurong
 * @date 2019-10-05
 */
public class Producer implements Runnable {
    private Product product;

    public Producer(Product product) {
        this.product = product;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {//模拟生成10个商品
            if (i % 2 == 0) {
                product.setProduct("Dove", "Chocolate");
            } else {
                product.setProduct("兰州黄河", "啤酒");
            }
        }
    }
}

3.定义一个消费者-实现Runnable方法使其具有多线程能力


/**
 * 消费者消费商品
 *
 * @author houxiurong
 * @date 2019-10-05
 */
public class Consumer implements Runnable {
    private Product product;

    public Consumer(Product product) {
        this.product = product;
    }

    @Override
    public void run() {
        //模拟消费10个商品
        for (int i = 1; i <= 10; i++) {
            product.getProduct();
        }
    }
}

4.开始测试

  /***
     *   +--> [信号灯] <---+
     *   |                |
     *   |                |
     * [生产者]         [消费者]
     *   |                |
     *   |                |
     *   +---> [商品] <---+
     */
    public static void main(String[] args) {
        //定义一个商品
        Product product = new Product();

        //生产者
        Producer producer = new Producer(product);

        //消费者
        Consumer consumer = new Consumer(product);

        //这里使用了静态代理设计模式, Thread相当于代理着
        new Thread(producer).start();
        new Thread(consumer).start();
    }

总结:

Java对象中有两种池:
锁池:一般理解为synchronized。
等待池:一般理解为wait,notify/notifyAll--必须在synchronize中才能使用,不然会抛出异常:

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	.....

1.如果线程调用了某个对象的wait方法,那么该线程进入到该对象的等待池中(并且将其获得的释放掉当前持有的锁);
2.如果未来的某一刻,另外一个线程调用了相同对象的notify方法或者notifyAll方法,那么该等待池中的线程就会被唤起,然后进入到对象的锁池里面去获得该对象的锁。
3.如果获的锁成功,那么该线程就会沿着wait方法之后的路径继续执行。(注意是沿着wait方法的之后的路径)