offer来了-阻塞队列

1.Java阻塞队列

队列是一种只允许在表的前端进行删除操作,而在表的后端进行插入操作的线性表。阻塞队列和一般队列的不同之处在于阻塞队列是“阻塞”的,这里的阻塞指的是操作队列的线程的一种状态。在阻塞队列中,线程阻塞有如下两种情况。

  • 消费者阻塞:在队列为空时,消费者端的线程都会被自动阻塞(挂起),直到有数据放入队列,消费者线程会被自动唤醒并消费数据
  • 生产者阻塞:在队列已满且没有可用空间时,生产者端的线程都会被自动阻塞(挂起),直到队列中有空的位置腾出,线程会被自动唤醒并生产数据。

2.阻塞队列的主要操作

阻塞队列的主要操作有插入操作和移除操作。插入操作有add(e)、offer(e)、put(e)、offer(e,time,unit),移除操作有remove()、poll()、take()、poll(time,unit),具体介绍如下。

插入操作

(1)public abstract boolean add(E paramE):将指定的元素插入队列中,在成功时返回true,如果当前没有可用的空间,则抛出IllegalStateException。如果该元素是null,则抛出NullPointerException异常。

(2)public abstract boolean offer(E paramE):将指定的元素插入队列中,在成功时返回true,如果当前没有可用的空间,则返回false。

(3)offer(E o, long timeout, TimeUnit unit):将指定的元素插入队列中,可以设定等待的时间,如果在设定的等待时间内仍不能向队列中加入元素,则返回false。

(4)public abstract void put(E paramE) throwsInterruptedException:将指定的元素插入队列中,如果队列已经满了,则阻塞、等待可用的队列空间的释放,直到有可用的队列空间释放且插入成功为止。

获取数据操作

(1)poll():取走队列队首的对象,如果取不到数据,则返回null。

(2)poll(long timeout, TimeUnit unit):取走队列队首的对象,如果在指定的时间内队列有数据可取,则返回队列中的数据,否则等待一定时间,在等待超时并且没有数据可取时,返回null。

(3)take():取走队列队首的对象,如果队列为空,则进入阻塞状态等待,直到队列有新的数据被加入,再及时取出新加入的数据。

(4)drainTo(Collection collection):一次性从队列中批量获取所有可用的数据对象,同时可以指定获取数据的个数,通过该方法可以提升获取数据的效率,避免多次频繁操作引起的队列锁定。

3.Java中的阻塞队列实现

Java中的阻塞队列有:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue、SynchronousQueue、LinkedTransferQueue、LinkedBlockingDeque。

  • ArrayBlockingQueue:基于数组结构实现的有界阻塞队列
  • LinkedBlockingQueue:基于链表结构实现的有界阻塞队列
  • PriorityBlockingQueue:基于优先级排序的无界阻塞队列
  • DelayQueue:基于优先级队列实现的无界阻塞队列
  • SynchronousQueue:基于控制互斥操作的阻塞队列
  • LinkedTransferQueue:基于链表结构实现的无界阻塞队列
  • LinkedBlockingDeque:基于链表结构实现的双向阻塞队列

4.ArrayBlockingQueue

ArrayBlockingQueue是基于数组实现的有界阻塞队列。ArrayBlockingQueue队列按照先进先出原则对元素进行排序,在默认情况下不保证元素操作的公平性。

队列操作的公平性指在生产者线程或消费者线程发生阻塞后再次被唤醒时,按照阻塞的先后顺序操作队列,即先阻塞的生产者线程优先向队列中插入元素,先阻塞的消费者线程优先从队列中获取元素。因为保证公平性会降低吞吐量,所以如果要处理的数据没有先后顺序,则对其可以使用非公平处理的方式。我们可以通过以下代码创建一个公平或者非公平的阻塞队列:

//大小为1000的公平队列
final ArrayBlockingQueue fairQueue=new ArrayBlockingQueue(1000,true);
//大小为1000的非公平队列
final ArrayBlockingQueue fairQueue=new ArrayBlockingQueue(1000,false);

5.LinkedBlockingQueue

LinkedBlockingQueue是基于链表实现的阻塞队列,同ArrayListBlockingQueue类似,此队列按照先进先出原则对元素进行排序;LinkedBlockingQueue对生产者端和消费者端分别采用了两个独立的锁来控制数据同步,我们可以将队列头部的锁理解为写锁,将队列尾部的锁理解为读锁,因此生产者和消费者可以基于各自独立的锁并行地操作队列中的数据,队列的并发性能较高。具体用法如下:

final LinkedBlockingQueue linkQueue=new LinkedBlockingQueue(100);

6.PriorityBlockingQueue

PriorityBlockingQueue是一个支持优先级的无界队列。元素在默认情况下采用自然顺序升序排列。可以自定义实现compareTo方法来指定元素进行排序规则,或者在初始化PriorityBlockingQueue时指定构造参数Comparator来实现对元素的排序。注意:如果两个元素的优先级相同,则不能保证该元素的存储和访问顺序。

7.DelayQueue

DelayQueue是一个支持延时获取元素的无界阻塞队列,在队列底层使用PriorityQueue实现。DelayQueue队列中的元素必须实现Delayed接口,该接口定义了在创建元素时该元素的延迟时间,在内部通过为每个元素的操作加锁来保障数据的一致性。只有在延迟时间到后才能从队列中提取元素。我们可以将DelayQueue运用于以下场景中。

  • 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素,则表示缓存的有效期到了。
  • 定时任务调度:使用DelayQueue保存即将执行的任务和执行时间,一旦从DelayQueue中获取元素,就表示任务开始执行,Java中的TimerQueue就是使用DelayQueue实现的。

在具体使用时,延迟对象必须先实现Delayed类并实现其getDelay方法和compareTo方法,才可以在延迟队列中使用。

8.SynchronousQueue

SynchronousQueue是一个不存储元素的阻塞队列。SynchronousQueue中的每个put操作都必须等待一个take操作完成,否则不能继续添加元素。我们可以将SynchronousQueue看作一个“快递员”,它负责把生产者线程的数据直接传递给消费者线程,非常适用于传递型场景,比如将在一个线程中使用的数据传递给另一个线程使用。SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue。

9.LinkedTransferQueue

LinkedTransferQueue是基于链表结构实现的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了transfer、tryTransfer和tryTransfer(E e, long timeout, TimeUnit unit)方法。

  • transfer方法:如果当前有消费者正在等待接收元素,transfer方法就会直接把生产者传入的元素投递给消费者并返回true。如果没有消费者在等待接收元素,transfer方法就会将元素存放在队列的尾部(tail)节点,直到该元素被消费后才返回。
  • tryTransfer方法:首先尝试能否将生产者传入的元素直接传给消费者,如果没有消费者等待接收元素,则返回false。和transfer方法的区别是,无论消费者是否接收元素,tryTransfer方法都立即返回,而transfer方法必须等到元素被消费后才返回。
  • tryTransfer(E e, long timeout, TimeUnit unit)方法:首先尝试把生产者传入的元素直接传给消费者,如果没有消费者,则等待指定的时间,在超时后如果元素还没有被消费,则返回false,否则返回true。

10.LinkedBlockingDeque

LinkedBlockingDeque是基于链表结构实现的双向阻塞队列,可以在队列的两端分别执行插入和移出元素操作。这样,在多线程同时操作队列时,可以减少一半的锁资源竞争,提高队列的操作效率。

LinkedBlockingDeque相比其他阻塞队列,多了addFirst、addLast、offerFirst、offerLast、peekFirst、peekLast等方法。以First结尾的方法表示在队列头部执行插入(add)、获取(peek)、移除(offer)操作;以Last结尾的方法表示在队列的尾部执行插入、获取、移除操作。

在初始化LinkedBlockingDeque时,可以设置队列的大小以防止内存溢出,双向阻塞队列也常被用于工作窃取模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值