zoukankan      html  css  js  c++  java
  • 并发编程(十二):阻塞队列


    1.阻塞队列简介

    阻塞队列是支持两个附加操作的队列(阻塞插入阻塞移除)

    • 阻塞插入:队列满时会阻塞插入元素的线程,直到队列不满
    • 阻塞移除:队列为空时,获取元素的线程会等待元素变为非空

    阻塞队列常用于生产者和消费者的场景

    阻塞队列不可用(阻塞)时插入和移除操作的四种处理方式:

    • 抛出异常:队列满再插入,队列空移除都会抛出异常
    • 返回特殊值:插入成功返回true,移除成功返回元素,否则为null
    • 一直阻塞:会等待队列不满或不空
    • 超时退出:线程等待一段时间后退出

    无界队列不会出现队列满的情况


    2.Java中的阻塞队列

    2.1 ArrayBlockingQueue

    由数组实现的有界阻塞队列,按照FIFO原则对元素进行排序

    默认情况下不是公平的(访问顺序与阻塞先后顺序无关)

    如下可创建一个公平的阻塞队列:

    ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);
    

    内部是通过可重入锁ReentrantLock实现公平性的

    2.2 LinkedBlockingQueue

    用链表实现的有界阻塞队列,默认和最大长度都是Integer.MAX_VALUE

    2.3 PriorityBlockingQueue

    支持优先级的无界阻塞队列,默认采用自然升序,不保证同优先级元素顺序

    自定义排序规则:

    • 可以实现元素compareTo()方法指定元素排序顺序
    • 也可以指定构造方法的参数Comparator指定排序规则

    2.4 DelayQueue

    支持延时获取元素的无界阻塞队列,使用PriorityQueue实现,队列中元素必须实现Delayed接口,创建元素时可以指定延时时长,只有延时时间满才能获取元素

    使用场景:

    • 缓存失效:用DelayQueue保存缓存元素有效期
    • 定时任务调度:用DelayQueue保存当天要执行的任务和执行时间

    如何实现Delayed接口

    1. 创建对象,初始化基本数据
    2. 实现getDelay方法,返回还需要延时多长时间
    3. 实现compareTo方法用来指定元素顺序

    实现延时阻塞队列

    从队列中获取元素时,如果元素没有达到延时时间,则拒绝

    long delay = first.getDelay(TimeUnit.NANOSECONDS); 
    if (delay <= 0)
        return q.poll(); 
    else if (leader != null)
        available.await(); 
    else {
        Thread thisThread = Thread.currentThread();
        leader = thisThread;
        try {
            //等待delay纳秒
            available.awaitNanos(delay);
        } finally {
            if (leader == thisThread)
                leader = null;
        }
    }
    

    2.5 SynchronousQueue

    不存储元素的阻塞队列,适合传递性场景。

    线程每一个put()操作必须等待一个take()操作,否则不能继续添加元素;多个线程的put()或take()操作会在等待队列中等待,支持公平访问,默认情况下是非公平的

    put等待:(公平锁)

    take等待:

    2.6 LinkedTransferQueue

    由一个链表结构组成的无界阻塞TranserQueue队列,相对于其他阻塞队列,多了tryTransfer和transfer方法

    transfer方法

    可以把生产者传入的元素立刻传输(transfer)给消费者,如果没有消费者在等待接收,就会将元素存放在队列的tail节点,等到元素被消费者接收了才返回

    tryTransfer方法

    试探能否直接将生产者传入的元素传给消费者,无论是否接收,都立即返回结果

    2.7 LinkedBlockingDeque

    由链表构成的双向阻塞队列,可以从队列的两端插入和移除元素

    初始化时可以设置容量防止其过度膨胀

    双向队列可用在"工作窃取"模式中


    3.阻塞队列实现原理

    阻塞队列主要是使用通知模式实现,以ArrayBlockingQueue为例

    ArrayBlockingQueue—>Condition.await—>LockSupport.park(this)—>unsafe.park()实现

    unsafe.park是个native方法:不同操作系统上有不同实现

    • Linux:使用系统方法pthread_cond_wait实现park
      • unpark方法使用pthread_cond_signal实现
    • Windows:使用 WaitForSignalObject 实现(Windows API)

  • 相关阅读:
    VS2005调试网站时不显示Flash元素
    js中使用弹出窗体
    Ipod Touch/Iphone歌词同步软件整理
    Chrome Dev 4.0.*增加flash支持
    字符串数组排序(qsort参数 比较函数)
    查找两个已经排好序的数组的第k大的元素
    求用1,2,5这三个数不同个数组合的和为100的组合个数
    Hadoop分布式环境下的数据抽样(转)
    Reservoir Sampling
    欧拉回路
  • 原文地址:https://www.cnblogs.com/kenshine/p/14520584.html
Copyright © 2011-2022 走看看