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)

  • 相关阅读:
    1014 Waiting in Line (30)(30 point(s))
    1013 Battle Over Cities (25)(25 point(s))
    1012 The Best Rank (25)(25 point(s))
    1011 World Cup Betting (20)(20 point(s))
    1010 Radix (25)(25 point(s))
    1009 Product of Polynomials (25)(25 point(s))
    1008 Elevator (20)(20 point(s))
    1007 Maximum Subsequence Sum (25)(25 point(s))
    1006 Sign In and Sign Out (25)(25 point(s))
    1005 Spell It Right (20)(20 point(s))
  • 原文地址:https://www.cnblogs.com/kenshine/p/14520584.html
Copyright © 2011-2022 走看看