BlockingQueue应用场景

更新时间:2023-08-11 04:14:25 阅读: 评论:0

BlockingQueue应⽤场景
举个⽐较常见的例⼦:
某电商有交易和邮件两套系统,当⼀个交易完成时,给⽤户发⼀封确认邮件。这⾥
交易系统和邮件系统显然是两个优先级的东西,交易系统要求很⾼的实时性,⽽邮件系统则不必,为了合理利⽤服务器资源,应当把这两套系统分离,合理的做法
是,交易完成时,交易系统⽣成⼀个发邮件的任务,丢到queue中,⽽邮件系统监听在queue的另⼀端,⽤⼀个相对较低的速度处理queue 中的发邮件
前⾔:
在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何⾼效安全“传输”数据的问题。通过这些⾼效并且线程安全的队列类,为我们快速搭建⾼质量的多线程程序带来极⼤的便利。本⽂详细介绍了BlockingQueue家庭中的所有成员,包括他们各⾃的功能以及常见使⽤场景。
认识BlockingQueue
阻塞队列,顾名思义,⾸先它是⼀个队列,⽽⼀个队列在数据结构中所起的作⽤⼤致如下图所⽰:
zoetrope从上图我们可以很清楚看到,通过⼀个共享的队列,可以使得数据由队列的⼀端输⼊,从另外⼀端输出;
常⽤的队列主要有以下两种:(当然通过不同的实现⽅式,还可以延伸出很多不同类型的队列,DelayQueue就是其中的⼀种)  先进先出(FIFO):先插⼊的队列的元素也最先出队列,类似于排队的功能。从某种程度上来说这种队列也体现了⼀种公平性。
  后进先出(LIFO):后插⼊队列的元素最先出队列,这种队列优先处理最近发⽣的事件。
多线程环境中,通过队列可以很容易实现数据共享,⽐如经典的“⽣产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若⼲⽣产者线程,另外⼜有若⼲个消费者线程。如果⽣产者线程需要把准备好的数据共享给消费者线程,利⽤队列的⽅式来传递数据,就可以很⽅便
地解决他们之间的数据共享问题。但如果⽣产者和消费者在某个时间段内,万⼀发⽣数据处理速度不匹配的情况呢?理想情况下,如果⽣产者产出数据的速度⼤于消费者消费的速度,并且当⽣产出来的数据累积到⼀定程度的时候,那么⽣产者必须暂停等待⼀下(阻塞⽣产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。然⽽,在
concurrent包发布以前,在多线程环境下,我们每个程序员都必须去⾃⼰控制这些细节,尤其还要兼顾效率和线程安全,⽽这会给我们的程序带来不⼩的复杂度。好在此时,强⼤的concurrent包横空出世了,⽽他也给我们带来了强⼤的BlockingQueue。(在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),⼀旦条件满⾜,被挂起的线程⼜会⾃动被唤醒)
下⾯两幅图演⽰了BlockingQueue的两个常见阻塞场景:shopaholic
如上图所⽰:当队列中没有数据的情况下,消费者端的所有线程都会被⾃动阻塞(挂起),直到有数据放⼊队列。
merci
   如上图所⽰:当队列中填满数据的情况下,⽣产者端的所有线程都会被⾃动阻塞(挂起),直到队列中有空的位置,线程被⾃动唤醒。
BlockingQueue的核⼼⽅法:
放⼊数据:
  offer(anObject):表⽰如果可能的话,将anObject加到BlockingQueue⾥,即如果BlockingQueue可以容纳,英翻译汉
    则返回true,否则返回fal.(本⽅法不阻塞当前执⾏⽅法的线程)
  offer(E o, long timeout, TimeUnit unit),可以设定等待的时间,如果在指定的时间内,还不能往队列中
    加⼊BlockingQueue,则返回失败。
  put(anObject):把anObject加到BlockingQueue⾥,如果BlockQueue没有空间,则调⽤此⽅法的线程被阻断
    直到BlockingQueue⾥⾯有空间再继续.
获取数据:
  poll(time):取⾛BlockingQueue⾥排在⾸位的对象,若不能⽴即取出,则可以等time参数规定的时间,
    取不到时返回null;home equity
  poll(long timeout, TimeUnit unit):从BlockingQueue取出⼀个队⾸的对象,如果在指定时间内,
    队列⼀旦有数据可取,则⽴即返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。
  take():取⾛BlockingQueue⾥排在⾸位的对象,若BlockingQueue为空,阻断进⼊等待状态直到
    BlockingQueue有新的数据被加⼊;
  drainTo():⼀次性从BlockingQueue获取所有可⽤的数据对象(还可以指定获取数据的个数),
    通过该⽅法,可以提升获取数据效率;不需要多次分批加锁或释放锁。
常见BlockingQueue
在了解了BlockingQueue的基本功能后,让我们来看看BlockingQueue家庭⼤致有哪些成员?
BlockingQueue成员详细介绍
1. ArrayBlockingQueue
基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了⼀个定长数组,以便缓存队列中的数据对象,这是⼀个常⽤的阻塞队列,除了⼀个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位
置。ArrayBlockingQueue在⽣产者放⼊数据和消费者获取数据,都是共⽤同⼀个锁对象,由此也意味着两者⽆法真正并⾏运⾏,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采⽤分离锁,从⽽实现⽣产者和消费者操作的完全并⾏运⾏。Doug Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写⼊和获取操作已经⾜够轻巧,以⾄于引⼊独⽴的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有⼀个明显的不同之处在于,前者在插⼊或删除元
素时不会产⽣或销毁任何额外的对象实例,⽽后者则会⽣成⼀个额外的Node对象。这在长时间内需要⾼效并发地处理⼤批量数据的系统中,其对于GC的影响还是存在⼀定的区别。⽽在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采⽤公平锁,默认采⽤⾮公平锁。
2. LinkedBlockingQueue
soup怎么读
基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着⼀个数据缓冲队列(该队列由⼀个链表构成),当⽣产者往队列中放⼊⼀个数据时,队列会从⽣产者⼿中获取数据,并缓存在队列内部,⽽⽣产者⽴即返回;只有当队列缓冲区达到最⼤值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞⽣产者队列,直到消费者从队列中消费掉⼀份数据,⽣产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。⽽LinkedBlockingQueue之所以能够⾼效的处理并发数据,还因为其对于⽣产者端和消费者端分别采⽤了独⽴的锁来控制数据同步,这也意味着在⾼并发的情况下⽣产者和消费者可以并⾏地操作队列中的数据,以此来提⾼整个队列的并发性能。
作为开发者,我们需要注意的是,如果构造⼀个LinkedBlockingQueue对象,⽽没有指定其容量⼤⼩,LinkedBlockingQueue会默认⼀个类似⽆限⼤⼩的容量(Integer.MAX_VALUE),这样的话,如果⽣产者的速度⼀旦⼤于消费者的速度,也许还没有等到队列满阻塞产⽣,系统内存就有可能已被消耗殆尽了。
ArrayBlockingQueue和LinkedBlockingQueue是两个最普通也是最常⽤的阻塞队列,⼀般情况下,在处理多线程间的⽣产者消费者问题,使⽤这两个类⾜以。
3. DelayQueue
数学专业排名
DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue是⼀个没有⼤⼩限制的队列,因此往队列中插⼊数据的操作(⽣产者)永远不会被阻塞,⽽只有获取数据的操作(消费者)才会被阻塞。
使⽤场景:
  DelayQueue使⽤场景较少,但都相当巧妙,常见的例⼦⽐如使⽤⼀个DelayQueue来管理⼀个超时未响应的连接队列。
4. PriorityBlockingQueue
基于优先级的阻塞队列(优先级的判断通过构造函数传⼊的Compator对象来决定),但需要注意的是
PriorityBlockingQueue并不会阻塞数据⽣产者,⽽只会在没有可消费的数据时,阻塞数据的消费者。因此使⽤的时候要特别注猫王经典歌曲
意,⽣产者⽣产数据的速度绝对不能快于消费者消费数据的速度,否则时间⼀长,会最终耗尽所有的可⽤堆内存空间。在实现PriorityBlockingQueue时,内部控制线程同步的锁采⽤的是公平锁。
rockwell
5. SynchronousQueue
全日制英语⼀种⽆缓冲的等待队列,类似于⽆中介的直接交易,有点像原始社会中的⽣产者和消费者,⽣产者拿着产品去集市销售给产品的最终消费者,⽽消费者必须亲⾃去集市找到所要商品的直接⽣产者,如果⼀⽅没有找到合适的⽬标,那么对不起,⼤家都在集市等待。相对于有缓冲的BlockingQueue来说,少了⼀个中间经销商的环节(缓冲区),如果有经销商,⽣产者直接把产品批发给经销商,⽽⽆需在意经销商最终会将这些产品卖给那些消费者,由于经销商可以库存⼀部分商品,因此相对于直接交易模式,总体来说采⽤中间经销商的模式会吞吐量⾼⼀些(可以批量买卖);但另⼀⽅⾯,⼜因为经销商的引⼊,使得产品从⽣产者到消费者中间增加了额外的交易环节,单个产品的及时响应性能可能会降低。
  声明⼀个SynchronousQueue有两种不同的⽅式,它们之间有着不太⼀样的⾏为。公平模式和⾮公平模式的区别:
  如果采⽤公平模式:SynchronousQueue会采⽤公平锁,并配合⼀个FIFO队列来阻塞多余的⽣产者和消费者,从⽽体系整体的公平策略;
  但如果是⾮公平模式(SynchronousQueue默认):SynchronousQueue采⽤⾮公平锁,同时配合⼀个LIFO队列来管理多余的⽣产者和消费者,⽽后⼀种模式,如果⽣产者和消费者的处理速度有差距,则很容易出现饥渴的情况,即可能有某些⽣产者或者是消费者的数据永远都得不到处理。
⼩结
  BlockingQueue不光实现了⼀个完整队列所具有的基本功能,同时在多线程环境下,他还⾃动管理了多线间的⾃动等待于唤醒功能,从⽽使得程序员可以忽略这些细节,关注更⾼级的功能。

本文发布于:2023-08-11 04:14:25,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/193451.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:队列   数据   产者   消费者
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图