一心多用多线程-阻塞队列(1)-ArrayBlockingQueue

线程阀是一种线程与线程之间互相制约,交互的机制。

在日常生活中呀,我们常常会遇到一些生产者消费者的问题,当我们去吃自助餐的时候,是厨师把菜做好(生产者提供),然后端到自助加菜区(阻塞队列),大家可以自己拿着盘子去加菜区里面加菜(消费者消耗)。厨师把菜做好了不是第一时间给到顾客的手里,而是放到一个区域里面,用户再根据自己所需到加菜区里面加菜。有时候厨师做的慢,那么顾客就得在选菜区等待厨师,有时候顾客吃的慢,那么厨师就得等待停止炒菜(线程阻塞)

那今天我们要讨论的是阻塞队列,也就是我们的选菜区,我们必须认识一下BlockingQueue接口,该接口规范了所有阻塞队列的基本操作。api摘录如下:

BlockingQueue 方法以四种形式出现,
对于不能立即满足但可能在将来某一时刻可以满足的操作,
这四种形式的处理方式不同:
第一种是抛出一个异常,
第二种是返回一个特殊值(nullfalse,具体取决于操作),
第三种是在操作可以成功前,无限期地阻塞当前线程,第四种是在放弃前只在给定的最大时间限制内阻塞。

下表中总结了这些方法:

一心多用多线程-阻塞队列(1)-ArrayBlockingQueue_第1张图片

定义了对阻塞队列操作的方法,我们可以按照我们具体的业务需求去调用具体的方法。

而且接口还对阻塞队列进行了一些非方法外的约束:
1.非空,生产者不能产出一个空对象
2.阻塞队列是可限制容量的
3.阻塞是线程安全的

介绍完了接口,我们来谈一谈具体的实现方式有哪些
一、底层为数组与链表的ArrayBlockingQueue与LinkedBlockingQueue的选菜区
其实这二者并没有多大的区别,只是底层数据缓存的数据结构不一样,一者是采用数组进行缓存,一者是根据链表进行缓存,都是构造成队列的形式。这两者都是有届的,就是说不会造成内存溢出问题。
这里谈到一个公平性问题,用自己的话讲就是是否每个消耗线程获取资源的概率都是相等的,还是存在诧异状况。这是由猴子自己定的,需要谈到一点的是,公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。

那这个选菜区到底是如何作用的呢?

public class BlockingQueueTest {
    public static void main(String[] args) {
        ArrayBlockingQueue valve = new ArrayBlockingQueue<>(10);// 定义一个数组阻塞队列,内部可存储空间为10个
        Thread fenOffer = new Thread(new Runnable() {// 定义一个生产线程
                    @Override
                    public void run() {
                        while (true) {
                            String s = "一盘肠粉";//资源
                            try {
                                valve.put(s);//采用的是阻塞添加形式
                            } catch (InterruptedException e1) {
                                e1.printStackTrace();
                            }// 提供一盘肠粉,当数组容量被填满时,就会被阻塞
                            System.out.println("生产了" + s);
                            try {
                                Thread.sleep(2000);// 每二秒生产一盘肠粉
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                        }
                    }
                }, "肠粉阿伯");
        Thread fenConsumer = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    String s = null;
                    try {
                        s = valve.take();//采用的是阻塞消耗的形式
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消耗了" + s);
                    try {
                        Thread.sleep(5000);//每5秒消耗一个资源
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        fenOffer.start();
        fenConsumer.start();

        while (true) {
            System.out.println("-------------------------------当前剩余肠粉:" + valve.size());//监视阻塞队列当前资源数
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

结果:

生产了一盘肠粉
-------------------------------当前剩余肠粉:1
消耗了一盘肠粉
-------------------------------当前剩余肠粉:0
-------------------------------当前剩余肠粉:0
生产了一盘肠粉
-------------------------------当前剩余肠粉:1
生产了一盘肠粉
-------------------------------当前剩余肠粉:2
-------------------------------当前剩余肠粉:2
消耗了一盘肠粉
生产了一盘肠粉
-------------------------------当前剩余肠粉:2
-------------------------------当前剩余肠粉:2
生产了一盘肠粉
-------------------------------当前剩余肠粉:3
-------------------------------当前剩余肠粉:3
生产了一盘肠粉
-------------------------------当前剩余肠粉:4
消耗了一盘肠粉
-------------------------------当前剩余肠粉:3
-------------------------------当前剩余肠粉:3
生产了一盘肠粉
-------------------------------当前剩余肠粉:4
-------------------------------当前剩余肠粉:4
生产了一盘肠粉
-------------------------------当前剩余肠粉:5
消耗了一盘肠粉
-------------------------------当前剩余肠粉:4
生产了一盘肠粉
-------------------------------当前剩余肠粉:5
-------------------------------当前剩余肠粉:5
生产了一盘肠粉
-------------------------------当前剩余肠粉:6
-------------------------------当前剩余肠粉:6
生产了一盘肠粉
消耗了一盘肠粉
-------------------------------当前剩余肠粉:6
-------------------------------当前剩余肠粉:6
生产了一盘肠粉
-------------------------------当前剩余肠粉:7
-------------------------------当前剩余肠粉:7
生产了一盘肠粉
-------------------------------当前剩余肠粉:8
消耗了一盘肠粉
-------------------------------当前剩余肠粉:7
生产了一盘肠粉
-------------------------------当前剩余肠粉:8
-------------------------------当前剩余肠粉:8
生产了一盘肠粉
-------------------------------当前剩余肠粉:9
生产了一盘肠粉
-------------------------------当前剩余肠粉:10
消耗了一盘肠粉
-------------------------------当前剩余肠粉:9
生产了一盘肠粉
-------------------------------当前剩余肠粉:10
-------------------------------当前剩余肠粉:10
-------------------------------当前剩余肠粉:10
-------------------------------当前剩余肠粉:10
消耗了一盘肠粉
生产了一盘肠粉
-------------------------------当前剩余肠粉:10
-------------------------------当前剩余肠粉:10
-------------------------------当前剩余肠粉:10
-------------------------------当前剩余肠粉:10

由此可见,当我们生产者生产的资源数占满了队列的时候,就会开始阻塞,并等待下次队列有空位时再进入生产,很好的解决了生产者消费者问题

你可能感兴趣的:(多线程与并发)