并发编程(十二):阻塞队列


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)

原文地址:https://www.cnblogs.com/kenshine/p/14520584.html