JUC知识点总结(五)生产者消费者模式的三种常见实现方法

11. 生产者\消费者模式

Object中的 wait()/notify()

要点:判断条件时一定要用while()循环(虚假唤醒)

public class Shop{
    public int count = 0;
    public void produce(){
        count++;
        System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
    }

    public void sell(){
        count--;
        System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
    }

    public static void main(String[] args){
        Shop shop = new Shop();
        Factory f1 =shop.new Factory(shop);
        Factory f2 = shop.new Factory(shop);
        Consumer c1 =shop.new Consumer(shop);
        Consumer c2 = shop.new Consumer(shop);
        f1.start();
        f2.start();
        c1.start();
        c2.start();
    }

    class Factory extends Thread{
        private Shop shop;
        Factory(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true) {
                synchronized (shop) {
                    while (shop.count >= 10) {
                        try {
                            shop.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    shop.produce();
                    shop.notifyAll();
                }

            }
        }
    }

    class Consumer extends Thread{
        private Shop shop;
        Consumer(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true){
                synchronized (shop) {
                    while (shop.count <= 0) {
                        try {
                            shop.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    shop.sell();
                    shop.notifyAll();
                }
            }
        }
    }
}

ReentryLock,Condition实现

public class Shop{
    private int count = 0;
    private Lock lock = new ReentrantLock();
    private Condition empty = lock.newCondition();
    private Condition full = lock.newCondition();
    public void produce(){
        count++;
        System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
    }

    public void sell(){
        count--;
        System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
    }

    public static void main(String[] args){
        Shop shop = new Shop();
        Factory f1 =shop.new Factory(shop);
        Factory f2 = shop.new Factory(shop);
        Consumer c1 =shop.new Consumer(shop);
        Consumer c2 = shop.new Consumer(shop);
        f1.start();
        f2.start();
        c1.start();
        c2.start();
    }

    class Factory extends Thread{
        private Shop shop;
        Factory(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.lock();
                try {
                    while (shop.count >= 10) {
                        try {
                            full.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    shop.produce();
                    empty.signal();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    class Consumer extends Thread{
        private Shop shop;
        Consumer(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true){
                try {
                    Thread.sleep(150);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.lock();
                try {
                    while (shop.count <= 0) {
                        try {
                            empty.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    shop.sell();
                    full.signal();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

阻塞队列实现

BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种:

  1. 当阻塞队列为空时,从阻塞队列中取数据的操作会被阻塞。
  2. 当阻塞队列为满时,往阻塞队列中添加数据的操作会被阻塞。

从上可知,阻塞队列是线程安全的。

JDK中的七大阻塞队列

阻塞队列名称 说明
ArrayBlockingQueue 一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue 一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue 一个支持优先级排序的无界阻塞队列。
DelayQueue 一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue 一个不存储元素的阻塞队列。
LinkedTransferQueue 一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque 一个由链表结构组成的双向阻塞队列。

ArrayBlockingQueue:
基于数组的阻塞队列实现,其内部维护一个定长的数组,用于存储队列元素。线程阻塞的实现是通过ReentrantLock来完成的,数据的插入与取出共用同一个锁,因此ArrayBlockingQueue并不能实现生产、消费同时进行。而且在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。

LinkedBlockingQueue:
基于单向链表的阻塞队列实现,在初始化LinkedBlockingQueue的时候可以指定对立的大小,也可以不指定,默认类似一个无限大小的容量(Integer.MAX_VALUE),不指队列容量大小也是会有风险的,一旦数据生产速度大于消费速度,系统内存将有可能被消耗殆尽,因此要谨慎操作。另外LinkedBlockingQueue中用于阻塞生产者、消费者的锁是两个(锁分离),因此生产与消费是可以同时进行的。

BlockingQueue接口的一些方法:

操作 抛异常 特定值 阻塞 超时
插入 add(o) offer(o) put(o) offer(o, timeout, timeunit)
移除 remove(o) poll(o) take(o) poll(timeout, timeunit)
检查 element(o) peek(o)

这四类方法分别对应的是:
1 . ThrowsException:如果操作不能马上进行,则抛出异常
2 . SpecialValue:如果操作不能马上进行,将会返回一个特殊的值,一般是true或者false
3 . Blocks:如果操作不能马上进行,操作会被阻塞
4 . TimesOut:如果操作不能马上进行,操作会被阻塞指定的时间,如果指定时间没执行,则返回一个特殊值,一般是true或者false

代码实现:

public class Shop{
    private int count = 0;
    private BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

    public void produce() {
        try {
            queue.put(1);
            count++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
    }

    public void sell(){
        try {
            queue.take();
            count--;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
    }

    public static void main(String[] args){
        Shop shop = new Shop();
        Factory f1 =shop.new Factory(shop);
        Factory f2 = shop.new Factory(shop);
        Consumer c1 =shop.new Consumer(shop);
        Consumer c2 = shop.new Consumer(shop);
        f1.start();
        f2.start();
        c1.start();
        c2.start();
    }

    class Factory extends Thread{
        private Shop shop;
        Factory(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                shop.produce();
            }
        }
    }

    class Consumer extends Thread{
        private Shop shop;
        Consumer(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true){
                shop.sell();
            }
        }
    }
}

下一篇
JUC知识点总结(六)多线程里的HashMap与ConcurrentHashMap源码分析

发布了54 篇原创文章 · 获赞 11 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/dong_W_/article/details/105060797