范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

快速掌握并发编程ArrayBlockingQueue底层原理和实战java

  背景
  在JDK1.5 的时候,在新增的 Concurrent 包中,BlockingQueue 很好的解决了多线程中,如何高效安全"传输"数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。
  本文详细介绍了BlockingQueue 家庭中的所有成员,包括他们各自的功能以及常见使用场景。分析ArrayBlockingQueue 的底层源码分析。
  BlockingQueue 简介
  阻塞队列,顾名思义,首先它是一个队列,而一个队列在数据结构中所起的作用大致如下图所示:
  从上图我们可以很清楚看到,通过一个共享的队列,可以使得数据由队列的一端输入,从另外一端输出。
  常用的队列主要有以下两种:(当然通过不同的实现方式,还可以延伸出很多不同类型的队列,DelayQueue 就是其中的一种)
  先进先出(FIFO):先插入的队列的元素也最先出队列,类似于排队的功能。从某种程度上来说这种队列也体现了一种公平性。
  后进先出(LIFO):后插入队列的元素最先出队列,这种队列优先处理最近发生的事件。
  下面两幅图演示了BlockingQueue 的两个常见阻塞场景:
  如上图所示:当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。
  如上图所示:当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。
  这也是我们在多线程环境下,为什么需要BlockingQueue 的原因。作为BlockingQueue 的使用者,我们再也不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue 都给你一手包办了。
  既然BlockingQueue 如此神通广大,让我们一起来见识下它的常用方法:
  BlockingQueue 的核心方法
  添加数据:
  offer(anObject):表示如果可能的话,将 anObject 加到 BlockingQueue 里,即如果 BlockingQueue 可以容纳,则返回 true,否则返回 false。(本方法不阻塞当前执行方法的线程)
  offer(E o, long timeout, TimeUnit unit):可以设定等待的时间,如果在指定的时间内,还不能往队列中加入 BlockingQueue,则返回失败。
  put(anObject):把 anObject 加到 BlockingQueue 里,如果 BlockingQueue 没有空间,则调用此方法的线程被阻断,直到 BlockingQueue 里面有空间再继续。
  获取数据:
  poll(time):取走 BlockingQueue 里排在首位的对象,若不能立即取出,则可以等 time 参数规定的时间,取不到时返回 null。
  poll(long timeout, TimeUnit unit):从 BlockingQueue 取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。
  take():取走 BlockingQueue 里排在首位的对象,若 BlockingQueue 为空,阻断进入等待状态直到 BlockingQueue 有新的数据被加入。
  drainTo():一次性从 BlockingQueue 获取所有可用的数据对象(还可以指定获取数据的个数), 通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。
  常见 BlockingQueue
  在了解了 BlockingQueue 的基本功能后,让我们来看看BlockingQueue 家庭大致有哪些成员?
  ArrayBlockingQueue 源码分析
  构造方法
  在ArrayBlockingQueue 中有三个构造方法,常用这两种,另外的这里就不说了,没多意义。
  //创造一个队列,指定队列容量就可以了,默认模式为非公平模式 public ArrayBlockingQueue(int capacity) {         this(capacity, false); } ////创造一个队列,指定队列容量和指定公平性 public ArrayBlockingQueue(int capacity, boolean fair) {         if (capacity <= 0)             throw new IllegalArgumentException();         this.items = new Object[capacity];         //这里用的就是重入锁,         //公平性通过fair来判断         lock = new ReentrantLock(fair);         //不为空条件队列,明显就是给消费者用         notEmpty = lock.newCondition();         //没有满条件队列,给生产者用         notFull =  lock.newCondition(); }
  复制代码
  重要属性
  //底层数据结构,说明底层存储数据的数据结构是数组 private final E[] items; //用来为下一个take/poll/remove的索引(出队) private int takeIndex; //用来为下一个put/offer/add的索引(入队) private int putIndex; //队列中元素的个数 private int count; /** Main lock guarding all access */    private final ReentrantLock lock;//锁 /** Condition for waiting takes */ private final Condition notEmpty;//等待出队的条件 /** Condition for waiting puts */ private final Condition notFull;//等待入队的条件
  复制代码
  从这几个属性就能看出来,ArrayBlockingQueue 的底层组成为:
  数组一个重入锁两个条件队列
  入队
  add()方法
  public boolean add(E e) {     // 调用父类的add(e)方法     return super.add(e); } public boolean add(E e) {     // 调用offer(e)如果成功返回true,如果失败抛出异常     if (offer(e))         return true;     else         throw new IllegalStateException("Queue full"); }
  复制代码
  offer(e)方法入队
  入参 e 为 null,会抛空指针异常
  元素插入到队尾(putIndex 自增)
  唤醒线程
  生产者消费者模式
  /**  * 在队尾插入一个元素,  * 如果队列没满,立即返回true;  * 如果队列满了,立即返回false  * 注意:该方法通常优于add(),因为add()失败直接抛异常  */ public boolean offer(E e) {     //检查存入的数据是否为null     checkNotNull(e);             final ReentrantLock lock = this.lock;     lock.lock();             try {                     //如果数组满了,返回入队失败          if (count == items.length)                             return false;                     else {                             //入队              enqueue(e);                             return true;                     }             } finally {                     lock.unlock();             } } //如果存入的数据是null的会抛控指针异常  private static void checkNotNull(Object v) {             if (v == null)   throw new NullPointerException();     } //putIndex是int类型,默认值就是0 private void enqueue(E x) {             final Object[] items = this.items;             //从下标为0的位置开始放入数据      items[putIndex] = x;             //如果putIndex等于数组大小,证明这是最后一个能存放的,然后把putIndex设置为0      //否则++putIndex      if (++putIndex == items.length){                     putIndex = 0;             }     //放入数据个数+1     count++;      /**       * 唤醒一个线程       * 如果有任意一个线程正在等待这个条件,那么选中其中的一个区唤醒。       * 在从等待状态被唤醒之前,被选中的线程必须重新获得锁       */     notEmpty.signal(); }
  复制代码
  带超时时间的 offer,在队尾插入一个元素,,如果数组已满,则进入等待,直到出现以下三种情况:-->阻塞
  被唤醒
  等待时间超时
  当前线程被中断
  put 方法入队
  public void put(E e) throws InterruptedException {             checkNotNull(e);             final ReentrantLock lock = this.lock;       //加锁,如果线程中断了抛出异常     lock.lockInterruptibly();             try {                     while (count == items.length){                //这里就是阻塞了,要注意。如果运行到这里,             //那么它会释放上面的锁,一直等到唤醒             notFull.await();               }             //放入队列         enqueue(e);             } finally {                     lock.unlock();             } }
  复制代码
  小结
  add(e)时如果队列满了则抛出异常;offer(e)时如果队列满了则返回 false;put(e)时如果队列满了则使用 notFull 等待;offer(e, timeout, unit)时如果队列满了则等待一段时间后如果队列依然满就返回 false;enqueue()利用放指针循环使用数组来存储元素;
  出队
  take 方法出队
  从头部出队
  队中没有数据,等待被唤醒再取数据。
  public E take() throws InterruptedException {     final ReentrantLock lock = this.lock;     //加锁,如果线程中断了抛出异常     lock.lockInterruptibly();             try {         //队列中不存元素         while (count == 0){               /*              * 一直等待条件notEmpty,即被其他线程唤醒              * (唤醒其实就是,有线程将一个元素入队了,然后调用notEmpty.signal()              * 唤醒其他等待这个条件的线程,同时队列也不空了)              */             notEmpty.await();         }         //否则出队         return dequeue();             } finally {                     lock.unlock();             }     } private E dequeue() {     final Object[] items = this.items;     // 取取指针位置的元素     E x = (E) items[takeIndex];     // 把取指针位置设为null     items[takeIndex] = null;     // 取指针前移,如果数组到头了就返回数组前端循环利用     if (++takeIndex == items.length)         takeIndex = 0;     // 元素数量减1     count--;     if (itrs != null)         itrs.elementDequeued();     // 唤醒notFull条件     notFull.signal();     return x; }
  复制代码
  poll 方法出队
  太简单了,如果队列里没有数据,就直接返回 null,否则从队列头部出队
  public E poll() {             final ReentrantLock lock = this.lock;             lock.lock();             try {         //如果队列里没有数据就直接返回null         //否则从队列头部出队         return (count == 0) ? null : dequeue();             } finally {                     lock.unlock();            }    }
  复制代码
  remove 方法
  public E remove() {     // 调用poll()方法出队     E x = poll();     if (x != null)         // 如果有元素出队就返回这个元素         return x;     else         // 如果没有元素出队就抛出异常         throw new NoSuchElementException(); }
  复制代码
  小结
  remove()时如果队列为空则抛出异常;poll()时如果队列为空则返回 null;take()时如果队列为空则阻塞等待在条件 notEmpty 上;poll(timeout, unit)时如果队列为空则阻塞等待一段时间后如果还为空就返回 null;dequeue()利用取指针循环从数组中取元素;
  小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【111】即可免费获取

王学兵带儿子买菜,大肚腩双下巴胡子拉碴不修边幅,儿子和诺一拼演技近日,有媒体爆出王学兵携儿子出门买菜的照片。照片中王学兵戴着黑色棒球帽和黑色边框眼镜,身穿黑色T恤和黑色裤子,儿子骑着自行车走在前面,他跟随在儿子后面。从近处看能看到王学兵最近长胖漫谈JODHPUR马裤焦特布尔与短靴的由来JODHPUR马裤焦特布尔与短靴的由来JODHPURS焦特布尔位于印度最大的邦RAJASTHAN拉贾斯坦的西部,RAJAPUTRA拉杰普特人的首领RaoJodha在1459年建立焦花朵盛开季节一起游玩不得不说,从古到今,出游赏花都是不可缺少的休闲娱乐活动,赏花确实是一件非常有益健康的活动。花是春天的信号,是美好的象征,没有人会拒绝它的存在,一年四季春为首,春天赏花因此也最是普通赵丽蓉出演西游记,全靠搭档念台词,杨洁导演您演错了还有人不知道赵丽蓉老师曾演过西游记吗?当初杨洁导演准备拍摄斗法降三怪这一集的时候,里面有位车迟国王后,杨洁导演觉得这个角色非赵丽蓉老师不可。在邀请她之前,杨洁导演十分忐忑,因为当时徐霞客游记之粤西游日记二徐霞客是兼具脚踏实地不浪漫情怀的行者。行走欣赏探究沿途的风景。对他。已不能仅仅弼结为爱好。已然是他存在的一种都式。终身旅行亲自考察。不只是科学探索也不是一般的寄情山水。更不是纯属奇Linux与windows共享文件的神器samba一什么是samba?搭建Samba服务器是为了实现Linux共享目录之后,在Windows可以直接访问该共享目录。现在介绍如何在ubuntu16。04系统中搭建Samba服务。二s春风十里,花下等你金展春暖花开时节,常会想起岭南节度使崔护的诗句去年今日此门中,人面桃花相应红。年少不懂诗中意。最初,常会去猜想结局会如何?于是,翻阅书籍查找资料,急切想找到答案。对于春天,自然多了NBA西部决赛第三场勇士胜独行侠5月22日,勇士队球员库里在投中三分球后庆祝。新华社美联当日,在20212022赛季NBA季后赛西部决赛第三场比赛中,金州勇士队客场以109比100战胜达拉斯独行侠队,总比分3比0推倒重建!CBA劲旅痛下决心外援主帅涉及10余人,41分天才来了CBA休赛期,没有精彩的赛事,对于大家来说,可能百无聊赖,偶尔去刷刷自己关心的球队,有没有最新的动态。而对于球队来说,休赛期才是最为关键的时刻。整个赛季,球队的成绩是好还是坏,这个东决G3,热火三军用命防守果然G3一开场,热火就展示了强硬的防守,人盯人,并且遇到对方突破,就整体收缩防线,移动积极。抢下后场篮板,快速推进,转换进攻,打对方的立足未稳,第一个进球就是斯特鲁斯的斜四十五大巴黎是不是太残忍?三大功臣先后被逼走,内马尔或许是下一个大巴黎是不是太残忍?三大功臣先后被逼走,内马尔或许是下一个!根据巴黎圣日耳曼官网5月21日消息,当日法甲迎来202122赛季的收官战,已经提前获得法甲冠军的巴黎圣日耳曼队主场以50
冬季最爱的懒人焖饭,简单又美味天气越来越冷,小动物冬眠了,人也变得越来越懒夏天早起收拾化妆的小姐妹,现在洗个脸出门都算是精致。以前饭饭经常赶早买菜,如今也总是下班顺手带上!天气暖和的时候在家做饭,我会很有耐心炒在海南的冬天我用醋涮火锅中国地道风物海南糟粕醋不是醋的醋糟粕醋是海南独有的一种食物,最早起源于海南文昌的铺前镇,作为民间小吃有着五百余年的历史。名为糟粕,其实为精华。酿酒时大米发酵余下的糟粕,继续发酵,发净水器存在的一些销售骗局,千万别上当净水器自进入市场以来不断推陈出新,各种净水技术和类型更新换代得非常快。面对复杂的净水市场,很多人在挑选净水器时都会被各种宣传弄得眼花缭乱不知道怎么选择,再加上一些销售骗局更是让一些联想推出YOGAPaper墨水平板,预售价2699元近日,联想正式推出了YOGAPapper墨水平板并已经上架开启预售,预售到手价为2699元。根据官方的介绍,YOGAPapper墨水平板搭载了一块10。3英寸自然光感墨水屏,支持色三星GalaxyXCover6Pro首次在ATampampampT上亮相,售价599。99美元三星GalaxyXCover6Pro在今年6月首次在德国发布,一直都没有对外销售。但近期,这款手机终于有了销售渠道,美国运营商ATT在官方网站上架了该款手机。目前,这款手机的销售仅谷歌首款折叠屏再曝光TensorG2芯片金属中框,售价或达1。2万谷歌的折叠屏手机PixelFold此前已被多次曝光,现在该产品出现在了Geekbench跑分平台单核1047分,多核3257分,运行Android13,配备12GB内存,从CPU规48款旗舰手机保值率对比华为苹果红米闭眼入手机中国新闻在人们现在的生活中,手机扮演了越来越重要的角色。并且手机的单价并不低,于是哪款手机保值率最高成为了人们关心的话题。消费者报道整理了2020年至今发布的48款旗舰手机,得全球AI正在指数发展ChatGPT终结搜索引擎,逆水寒手游要革新NPC人工智能,对于绝大多数网友来说都是一个充满科幻色彩的词汇。但是一个显而易见的事实却是,全球的AI技术正在以指数级的速度发展,并且已经进入了互联网生活的方方面面。2015年至2021教育与科技双向奔赴一场无声无息的变革21世纪经济报道记者白杨北京报道对教育行业而言,2021年是一个重要转折点。双减政策的发布,让线下学科类培训机构迎来剧变,或转型,或离场,一时间,业内人心惶惶教育行业到底还能不能坚反思科学与信仰的深刻意义一般认为科学与信仰是两条不同的道路。科学追求实证性的知识,讲求可检验性可重复性和逻辑的严密性而信仰则不然,是对崇高神的信念。历史上,科学与信仰曾经是不相容的,起初是宗教对科学的禁锢Windows端也跟进了!基于Electron框架全新QQ内测版明年推出目前,QQ的macOS版已经开始采用Electron框架,而Linux版也将推出基于该框架的新测试版。现在,根据腾讯QQ项目组消息,Windows端也将跟进底层架构更新,推出同样采