zoukankan      html  css  js  c++  java
  • 阻塞队列分析

    转载请标注来源:https://www.cnblogs.com/xmzJava/p/9380649.html

    前言


     在分析多线程的文章中,我们知道了Executors是通过阻塞队列接受任务。例如 FixedThreadPool 使用的是 LinkedBlockingQueue, CachedThreadPool 使用的是 SynchronousQueue。阻塞队列的基类是 BlockingQueue,他的实现类如下所示

     

    BlockingQueue的api我们需要重点关注下,理解了这些api的作用,对于实现类的分析会轻松很多。

    类型 api名称 是否阻塞 简述
    放入数据 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, TimeUnit unit) 同上
    take() 取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入
    drainTo() 一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁

    重点关注表中的阻塞类型的方法,他们是阻塞队列的核心。接下来讲述几个常用的阻塞队列的存取数据api。

    ArrayBolckingQueue


    在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象。我们先看offer方法

    我们再看下 enqueue

     

    以上是非阻塞的存放。我们再看阻塞版本的存放

    可以看到,两种方式的差别其实很小,一个是阻塞一段时间后直接返回false,一个是无限期的阻塞。我们再看下取数据的api。

    再看下dequeue

     以上就是ArrayBlockingQunue的具体分析。

    LinkedBlockingQueue


     LinkedBlockingQueue是基于链表的阻塞队列,首先需要注意的是LinkedBlockingQueue是可以无界的,当你不指定容量时他默认的大小是 Integer.MAX_VALUE

    链表通过内部的Node来实现,可以看出这是个单项链表。

    接下来,我们再看下具体的api 

    我们看下 enqueue,就是一个简单的链表操作。

    put的操作和offer相似,这里就省略了,再看下poll

    看下dequeue,把头结点取出来,把下个节点设为头结点

    以上就是LinkedBlockingQueue。

    通过上述两种队列的讲解,我们大概知道了队列存取元素的大致过程,其他队列和上述两种队列的api大致相同,所以接下重点讲述队列的大致特点,不再对api进行详细的描述。

    PriorityQueue


         PriorityQueue并不是阻塞队列,在这里讲述是因为,接下来的几个队列都是基于他的扩展。PriorityQueue和ArrayBlockingQueue一样,内部维护了一个定长数组,如果不指定长度,默认长度就是11。PriorityQueue是一个优先级队列,元素的顺序并不是按照插入顺序而来,而是按照元素的大小来判断。默认的比较器是从小到大,即队首的元素总是最小的。当然。可以自定义比较器。

        PriorityQueue的优先级通过二叉小顶堆实现,他的逻辑结构是一棵完全二叉树,存储结构其实是一个数组。逻辑结构层次遍历的结果刚好是一个数组。这里借鉴网上的一幅图

     

    我们看下添加元素的过程,这里重点是siftUp方法

                

    由上图可以看出,PriorityQunue 最关键的便是 比较-交换 步骤。 添加元素都是先放到最后,然后再与自己的父节点比较

    再看下获取元素的步骤

          

     这里还有种情况,就是方法,删除中间的某个元素,这就是上述两种变化的结合,首先删除 i 下标的元素,然后把末尾的元素放置到 i 坐标,先向下比较看看,再向上比较。具体的代码这里就省略了。

     以上就是PriorityQueue的具体分析。

    DelayQueue


    DelayQueue 是延迟阻塞队列,队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue是一个没有大小限制的队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

    DelayQueue内部通过PriorityQueue实现延迟的权重排序 ,其比较器是 Comparable<Delayed> 

    主要看存元素的api

    这里关键的是  

    first.getDelay(NANOSECONDS)>0

     所以我们在实现 Delayed的时候 实现类里面一定得要有时间可以记录到什么过期,如果过期了一定要返回负数

    DelayQueue平时比较少见,但是我们可以用它做一些很灵活的事情,例如缓存过期,空闲连接去除。按照DelayQueue的特性,队首的元素总是最先过期的,我们可以用一个后台线程监听DelayQueue的队首元素。

    这里  大家可以参考下用 DelayQueue实现一个过期缓存清除的功能。

    PriorityBlockingQueue


     PriorityBlockingQueue是优先级阻塞队列,他和我们上文讲的PriorityQueue非常相似。但是呢他又多了点阻塞的东西,准确来说是多了半点。因为在做put操作的时候是不会有阻塞的

    就算我们给他一个初始大小,但是如果容量不够还会去扩容

    所以我们在往里面put的时候要注意,万一生产者一直在生产,消费者挂了,那么内存很容易就会被耗尽

    SynchronousQueue


     在线程池里面,大家一定见到过这个队列。这是一个不直接存放元素的队列,他存储的实际上是他内置的Node。它生产产品(即put的时候),如果当前没有人想要消费产品(即当前没有线程执行take),此生产线程必须阻塞,等待一个消费线程调用take操作,take操作将会唤醒该生产线程,同时消费线程会获取生产线程的产品。具体的流程我举个例子

    步骤一,调用put线程,存储元素

    步骤二,再次调用put线程,存储元素 ,在调用一个take线程,取出一个元素

    这个时候take线程会和put("b")配对,配对成功后put("a")线程就会成为头结点,整个流程如下图所示

    SynchronousQueue有两种模式,公平和非公平,默认是非公平的。内部有两种数据结构,分别是是队列和栈来表示公平和非公平两种模式。上图所描述的是非公平模式,即先进后出。具体细节还是得从代码里看。

    首先看下存取元素的api

    两个方法都调用的  transfer 方法,这就是一个配对的方法。这里截取核心部分的transfer方法供大家参考

    这里采用了大量的CAS操作进行更新,初看有点乱,但是心中熟记这是个配对方法,再debug几次。代码就会很清晰了。


     参考:

    https://blog.csdn.net/u013309870/article/details/71189189 【Java堆结构PriorityQueue完全解析】

    http://www.cnblogs.com/leesf456/p/5560362.html 【SynchronousQueue分析】

  • 相关阅读:
    积少成多Flash(8) ActionScript 3.0 网页之获取参数,JavaScript与ActionScript之间的相互调用
    积少成多Flash(11) Flex 3.0 动画效果(effect)
    积少成多Flash(9) Flex 3.0 布局控件, 样式(css), 皮肤(skin)
    系出名门Android(8) 控件(View)之TextSwitcher, Gallery, ImageSwitcher, GridView, ListView, ExpandableList
    系出名门Android(9) 数据库支持(SQLite), 内容提供器(ContentProvider)
    积少成多 Flash(ActionScript 3.0 & Flex 3.0) 系列文章索引
    系出名门Android(5) 控件(View)之TextView, Button, ImageButton, ImageView, CheckBox, RadioButton, AnalogClock, DigitalClock
    系出名门Android(1) 在 Windows 下搭建 Android 开发环境,以及 Hello World 程序
    小程序webview组件 nothing
    Python3的bytes/str之别
  • 原文地址:https://www.cnblogs.com/xmzJava/p/9380649.html
Copyright © 2011-2022 走看看