多线程阻塞队列

阻塞队列简介

        阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

常见阻塞场景

阻塞队列有两个常见的阻塞场景,它们分别是:

(1)当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。

(2)当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。支持以上两种阻塞场景的队列被称为阻塞队列。

在Java中提供了7个阻塞队列,它们分别如下所示。

•ArrayBlockingQueue:由数组结构组成的有界阻塞队列。

•LinkedBlockingQueue:由链表结构组成的有界阻塞队列。

•PriorityBlockingQueue:支持优先级排序的无界阻塞队列。

•DelayQueue:使用优先级队列实现的无界阻塞队列。

•SynchronousQueue:不存储元素的阻塞队列。

•LinkedTransferQueue:由链表结构组成的无界阻塞队列。

•LinkedBlockingDeque:由链表结构组成的双向阻塞队列。

       1.ArrayBlockingQueue它是用数组实现的有界阻塞队列,并按照先进先出(FIFO)的原则对元素进行排序。默认情况下不保证线程公平地访问队列。公平访问队列就是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列。即先阻塞的生产者线程,可以先往队列里插入元素;先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量。我们可以使用以下代码创建一个公平的阻塞队列。

       2.LinkedBlockingQueue它是基于链表的阻塞队列,同ArrayListBlockingQueue类似,此队列按照先进先出(FIFO)的原则对元素进行排序,其内部也维持着一个数据缓冲队列(该队列由一个链表构成)。当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到缓存容量的最大值时(LinkedBlockingQueue可以通过构造方法指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒。反之,对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效地处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步。这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE)。这样一来,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。ArrayBlockingQueue和LinkedBlockingQueue是两个最普通也是最常用的阻塞队列。一般情况下,在处理多线程间的生产者-消费者问题时,使用这两个类足已。

        3.PriorityBlockingQueue它是一个支持优先级的无界队列。默认情况下元素采取自然顺序升序排列。这里可以自定义实现compareTo()方法来指定元素进行排序规则;或者初始化PriorityBlockingQueue时,指定构造参数Comparator来对元素进行排序。但其不能保证同优先级元素的顺序。

        4.DelayQueue它是一个支持延时获取元素的无界阻塞队列。队列使用PriorityQueue来实现。队列中的元素必须实现 Delayed 接口。创建元素时,可以指定元素到期的时间,只有在元素到期时才能从队列中取走。

        5.SynchronousQueue它是一个不存储元素的阻塞队列。每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。因此此队列内部其实没有任何一个元素,或者说容量是0,严格来说它并不是一种容器。由于队列没有容量,因此不能调用peek操作(返回队列的头元素)。

        6.LinkedTransferQueue它是一个由链表结构组成的无界阻塞TransferQueue队列。LinkedTransferQueue实现了一个重要的接口TransferQueue。该接口含有5个方法,其中有3个重要的方法,它们分别如下所示。

        (1)transfer(E e):若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;如果没有消费者在等待接收数据,就会将元素插入到队列尾部,并且等待进入阻塞状态,直到有消费者线程取走该元素。

        (2)tryTransfer(E e):若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;若不存在,则返回 false,并且不进入队列,这是一个不阻塞的操作。与 transfer 方法不同的是,tryTransfer方法无论消费者是否接收,其都会立即返回;而transfer方法则是消费者接收了才返回。

        (3)tryTransfer(E e,long timeout,TimeUnit unit):若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;若不存在则将元素插入到队列尾部,并且等待消费者线程取走该元素。若在指定的超时时间内元素未被消费者线程获取,则返回false;若在指定的超时时间内其被消费者线程获取,则返回true。

        7.LinkedBlockingDeque它是一个由链表结构组成的双向阻塞队列。双向队列可以从队列的两端插入和移出元素,因此在多线程同时入队时,也就减少了一半的竞争。由于是双向的,因此LinkedBlockingDeque多了addFirst、addLast、offerFirst、offerLast、peekFirst、peekLast等方法。其中,以First单词结尾的方法,表示插入、获取或移除双端队列的第一个元素;以Last单词结尾的方法,表示插入、获取或移除双端队列的最后一个元素。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟、。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值