生产者消费者模型 学习

简介

得知并发是Java程序员进阶的必经之路,所以从实际代码来先理解 生产者消费者模型

实战

Demo File

package demo;

/**
 * 定义商品
 * 
 * @author draymonder
 *
 */
public class Goods {
    public final String name;
    public final int price;
    public final int id;

    // public Goods() {
    // }

    public Goods(String name, int price, int id) {
        this.name = name;
        this.price = price;
        this.id = id;
    }

    @Override
    public String toString() {
        return "name: " + name + ", price: " + price + ", id: " + id;
    }
}
package demo;

import java.util.Random;

/***
 * 线程对象,一个缓存位置,一个生产者,一个消费者,无限生产商品消费商品 流通的商品最多只有一个
 * 
 * @author draymonder
 *
 */
public class ProductComsumerDemo1 {
    // 定义一个商品缓存位置
    private volatile Goods goods;

    // 定义一个对象作为锁,不使用goods作为锁是因为生产者每次回产生一个新的对象
    private Object obj = new Object();

    // isNew == true 生产者线程休息,消费者线程消费
    // isNew == false 消费者线程休息,生产者线程生产
    private volatile boolean isNew = false;

    // 商品id编号 每生产一个id自增1
    private int id = 1;

    // 随机产生一个sleep时间
    private Random rnd = new Random();

    // 消费者线程
    public class ComsumeThread implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    synchronized (obj) {
                        // 当前没有商品
                        if (!isNew) {
                            obj.wait();
                        }
                        // 延迟时间
                        Thread.sleep(rnd.nextInt(250));
                        // 模拟消费产品
                        System.out.println(goods);
                        // 延迟时间
                        Thread.sleep(rnd.nextInt(250));

                        isNew = false;
                        // 唤醒阻塞obj上的生产者线程
                        obj.notify();
                    }
                    Thread.sleep(rnd.nextInt(250));
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    }

    // 生产者线程
    public class ProductThread implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    synchronized (obj) {
                        // 当前有新的产品没有被消费,需要先消费再生产
                        if (isNew) {
                            obj.wait();
                        }
                        // 延迟时间
                        Thread.sleep(rnd.nextInt(250));

                        if (id % 2 == 0) {
                            goods = new Goods("商品A" + id, id, id);
                        } else {
                            goods = new Goods("商品B" + id, id, id);
                        }
                        id++;
                        isNew = true;
                        // 延迟时间
                        Thread.sleep(rnd.nextInt(250));
                    }
                }
            } catch (Exception e) {
                // 什么都不做
            }
        }
    }

    public static void main(String[] args) {
        ProductComsumerDemo1 demo1 = new ProductComsumerDemo1();
        Runnable consume = demo1.new ComsumeThread();
        Runnable product = demo1.new ProductThread();
        // 先生产
        new Thread(product).start();
        
        // 后消费
        new Thread(consume).start();
        //new Thread(consume).start();
        //new Thread(consume).start();
    }

}

上述代码 构建了一个生产者,一个消费者,并且最多只有一个商品的流通的并发情况。

多个生产者 多个消费者 一个商品的流通

将上述main函数中的生产者,消费者多定义几个,是不是就可以了呢?

public static void main(String[] args) {
    ProductComsumerDemo1 demo1 = new ProductComsumerDemo1();
    Runnable consume = demo1.new ComsumeThread();
    Runnable product = demo1.new ProductThread();
    // 先生产
    new Thread(product).start();
    
    // 后消费
    new Thread(consume).start();
    new Thread(consume).start();
    new Thread(consume).start();
}

执行结果

name: 商品B1, price: 1, id: 1
name: 商品A2, price: 2, id: 2
name: 商品A2, price: 2, id: 2
name: 商品A2, price: 2, id: 2
name: 商品B3, price: 3, id: 3
name: 商品A4, price: 4, id: 4
name: 商品A4, price: 4, id: 4
name: 商品A4, price: 4, id: 4
name: 商品B5, price: 5, id: 5
name: 商品A6, price: 6, id: 6
name: 商品A6, price: 6, id: 6

可以观察到 单个id商品存在重复使用的情况,为什么呢?

现在我们来分析一下原因。当生产者生产好了商品,会唤醒因没有商品而阻塞消费者线程,假设唤醒的消费者线程超过两个,这两个线程会竞争获取锁,获取到锁的线程就会从obj.wait()方法中返回,然后消费商品,并把isNew置为false,然后释放锁。当被唤醒的另一个线程竞争获取到锁了以后也会从obj.wait()方法中返回。会再次消费同一个商品。
显然,每一个被唤醒的线程应该再次检查isNew这个条件。
所以无论是消费者,还是生产者,isNew的判断必须改成while循环,这样才能得到正确的结果而不受生产者的线程数和消费者的线程数的影响。

而对于只有一个生产者线程,一个消费者线程,用if判断是没有问题的,但是仍然强烈建议改成while语句进行判断。

package demo;

import java.util.Random;

/***
 * 线程对象,一个缓存位置,一个生产者,一个消费者,无限生产商品消费商品 流通的商品最多只有一个
 * 
 * @author draymonder
 *
 */
public class ProductComsumerDemo1 {
    // 定义一个商品缓存位置
    private volatile Goods goods;

    // 定义一个对象作为锁,不使用goods作为锁是因为生产者每次回产生一个新的对象
    private Object obj = new Object();

    // isNew == true 生产者线程休息,消费者线程消费
    // isNew == false 消费者线程休息,生产者线程生产
    private volatile boolean isNew = false;

    // 商品id编号 每生产一个id自增1
    private int id = 1;

    // 随机产生一个sleep时间
    private Random rnd = new Random();

    // 消费者线程
    public class ComsumeThread implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    synchronized (obj) {
                        // 当前没有商品
                        while (!isNew) {
                            obj.wait();
                            System.out.println(Thread.currentThread().getName() + " 正在wait");
                        }
                        // 延迟时间
                        Thread.sleep(rnd.nextInt(250));
                        // 模拟消费产品
                        System.out.println(goods);
                        // 延迟时间
                        Thread.sleep(rnd.nextInt(250));

                        isNew = false;
                        // 唤醒阻塞obj上的生产者线程
                        obj.notify();
                    }
                    Thread.sleep(rnd.nextInt(250));
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    }

    // 生产者线程
    public class ProductThread implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    synchronized (obj) {
                        // 当前有新的产品没有被消费,需要先消费再生产
                        while (isNew) {
                            obj.wait();
                            System.out.println(Thread.currentThread().getName() + " 正在wait");
                        }
                        // 延迟时间
                        Thread.sleep(rnd.nextInt(250));

                        if (id % 2 == 0) {
                            goods = new Goods("商品A" + id, id, id);
                        } else {
                            goods = new Goods("商品B" + id, id, id);
                        }
                        id++;
                        isNew = true;
                        // 延迟时间
                        Thread.sleep(rnd.nextInt(250));
                    }
                }
            } catch (Exception e) {
                // 什么都不做
            }
        }
    }

    public static void main(String[] args) {
        ProductComsumerDemo1 demo1 = new ProductComsumerDemo1();
        Runnable consume = demo1.new ComsumeThread();
        Runnable product = demo1.new ProductThread();
        // 先生产
        new Thread(product).start();
        new Thread(product).start();
        new Thread(product).start();
        
        
        // 后消费
        new Thread(consume).start();
        // new Thread(consume).start();
        // new Thread(consume).start();
    }

}

猜你喜欢

转载自www.cnblogs.com/Draymonder/p/10192084.html
今日推荐