Java多线程/并发26、阻塞队列BlockingQueue

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/soonfly/article/details/71330825

BlockingQueue接口定义了一种队列,这种队列通常容量是提前固定(确定了容量大小)的。容量满时往BlockingQueue中添加数据时会造成阻塞,容量为空时取元素操作会阻塞。

我们可以认为BlockingQueue队列是一个水库。水库满了的时侯,上游的水就要被拦住,不能再往水库里灌了。平时农庄浇灌,生活饮用都用这里面的水。如果水库干了,那么要灌溉的工人只能等着上游放水后,才能继续工作。
实际上我们已经用Condition实现过一次了,见《利用Condition来实现阻塞队列》。

BlockingQueue多用于生产-消费模式。还记得Future兑换月饼的例子吗?里面用到的CompletionService的实现用的就是一个保存Future对象的BlockingQueue

在JUC中,BlockingQueue接口有以下实现:
这里写图片描述
ArrayBlockingQueue基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量putIndex和takeIndex,分别标识着队列的头部和尾部在数组中的位置。
这里写图片描述

我们用ArrayBlockingQueue改写一下领月饼的程序:

public class ArrayBlockingQueueDemo {
    public static void main(String[] args) {
        final BlockingQueue<Integer> abqueue=new ArrayBlockingQueue<Integer>(10);
        /* 定义生产者:用来做月饼的Callable */
        final Runnable runnable = new Runnable() {
            public void run() {
                try {
                    /*模拟耗时操作,需要5秒*/
                    Thread.sleep(5000);
                    /*在队列中放上一盒做好的月饼编号*/
                    abqueue.put(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        /*开启线程B--消费者:获取月饼*/
        Runnable constmor_runnable=new Runnable() {
            public void run() {
                try {
                     ExecutorService tPool = Executors.newCachedThreadPool();
                     System.out.println("老板,给我开始做月饼...");
                     /*启动线程A--生产者:运行耗时操作,三条生产线开始生产月饼*/
                    for(int i=0;i<3;i++){
                        tPool.submit(runnable);
                    }
                     /*拿月饼*/
                    System.out.println("5秒钟后......");
                    for(int i=0;i<3;i++){
                        /*生产月饼需要5秒,这时线程运行到take()这里,还没有月饼,因此就在这里阻塞等待。*/
                        System.out.println("用月饼券兑换到月饼,该盒月饼编号:"+abqueue.take());
                    }
                     System.out.println("拿饼回家...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        new Thread(constmor_runnable).start();
    }
}

另外再说说LinkedBlockingQueue:
LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。
它与ArrayBlockingQueue与使用一样,上例中可以直接用LinkedBlockingDeque替换

附:
BlockingQueue接口常用方法:
放入数据:

  • offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.(本方法不阻塞当前执行方法的线程)
  • offer(E o, long timeout, TimeUnit unit),可以设定等待的时间,如果在指定的时间内,还不能往队列中加入BlockingQueue,则返回失败。
  • put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.

获取数据:

  • poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null;
  • poll(long timeout, TimeUnitunit):从BlockingQueue取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。
  • take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入;
  • drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。

猜你喜欢

转载自blog.csdn.net/soonfly/article/details/71330825