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

快速掌握并发编程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】即可免费获取

声震天下,势吞金乌DC评兴戈APT7日蚀又称日食,一种天文现象,相传佛教释迦摩尼的一位弟子之母,生性暴戾。玉帝得知,将其打入十八层地狱,变为恶狗,永世不得超生。弟子日夜修炼,成了地藏菩萨。为救母亲,他用锡杖打开地狱门真无线耳机也能有箱子味?DC评惠威HiViAW76对于玩过音响的朋友们来说,惠威这个品牌一定不陌生。作为民族企业,经过30年的发展,惠威已经成为世界著名高级音响制造公司,并以各类杰出的电声产品享誉业界。而近年来,惠威也顺应时代发展如期而至,很有精神耳机美学直击2020中国广州国际耳机展(图Envied文EnviedDC)今年首个如期举办的大型HiFi盛会2020中国(广州)国际耳机展终于在万众期盼之中于9月1213日在白云国际会议中心圆满举办了。今年的展会耳机美钢铁之心DC评水月雨SSR超级银船如果你想买个入门价位的耳机,你会买什么?这几年有粉丝求推荐入门耳机,我的答案往往是水月雨。原因很简单,水月雨是一个拥有广泛群众基础的品牌,对于这样的品牌,除非它想砸自己招牌,否则它神说,要有光DC评水月雨Illumination光神说要有光就有了光。圣经以这一句话开启。上帝也曾说我是世界的光。跟从我的,就不在黑暗里行走,必要得着生命的光。光是希望公义良善荣耀。有了光,才有这世间万物,才有这芸芸众生。水月雨的直男情书DC评LZA7和老忠初次见面是在一年半以前我搞的线下聚会上,印象中的老忠就是一个内敛的中年大哥,一如他的产品般朴实无华。他创立的LZHIFI在国内一直可谓默默无闻,甚至有的烧友都没有听说过,但是如期而至,很有精神耳机美学直击2020中国国际耳机展中篇413锦锋音响北京安润在展厅门口的原创入门新品OPAQ2。1成为大家试听较多的对象。这是一台兼顾桌面发烧系统和小型无源音箱系统的一体化解码耳放功放一体机,在成都和上海展上备受关注,动听音符,动人频率DC评7Hzi88致命频率第一次听到七赫兹7Hz这个品牌的时候,我是有一丝诧异的。为什么呢,是因为众所周知,人耳能听到的频率范围是20Hz至20000Hz之间,所以,7Hz这个频响人耳是根本听不到的如期而至,很有精神耳机美学直击2020中国国际耳机展下篇C厅展位C01享声SOUNDAWAREC02NFAudio宁梵声学NM2的声音十分的正,理性之中又包含着感性,并不会显得人声冷冰冰。NM2的外观十分的好看,如同声音一样的清澈透明。1113DC叔评海贝R2FD1Beans海贝R2FD1Beans应该是我玩过的最亲民的官方西装三件套了。官方套装最大的意义就在于从设计之初就根据彼此做好了调音匹配和优化,能最大程度上发挥出整套设备的实力,另外统一的设计语HTC开发元宇宙平台XR企业当红齐天获数亿元人民币融资(VRPinea10月14日讯)今日重点新闻HTC目前正在开发一款名叫ViveportVerse的元宇宙平台小米建银国际领投,XR企业当红齐天完成B轮数亿元融资乌克兰元宇宙虚拟活动
光伏新动力光伏电池片技术百舸争渡!行业发展竞得满园香?在冉冉升起的光伏风电锂电等新能源赛道,上游新能源设备企业技术储备业务发展更为外界瞩目。以光伏电池为例,当下光伏技术变革面临由P型电池转向N型电池(TOPConHJTIBC)过渡阶段铁骑力士集团副董事长雷博打破固有思维开启农牧食品行业创新经营新经济领军人物封面新闻记者朱珠3月29日下午,由四川省经信厅四川省工商联四川日报报业集团作为指导单位,封面新闻华西都市报封面科技主办的榜样中国2021四川十大经济影响力人物评选活动颁奖典礼在成都黑客从视频游戏AxieInfinity的Ronin网络中窃取了超过6亿美元最新的加密黑客攻击针对的是一个以游戏为中心的区块链网络,该网络支持流行的视频游戏AxieInfinity。黑客利用价值约6。25亿美元的以太坊和USDC这两种加密货币,成为有史以来数字经济下金融科技发展之路数字经济是当前我国经济增长的核心动力之一。我国十四五规划提出锚定2035年远景目标,加快数字化发展,建设数字中国,打造数字经济新优势。近期,金融科技发展规划(20222025年)关36氪首发毫米波雷达芯片公司正和微芯获数千万天使轮融资,让消费电子拥有自动驾驶级的感知36氪获悉,毫米波雷达智能感知芯片及系统解决方案提供商正和微芯(Possumic)完成天使轮数千万元融资,本轮由达泰资本领投,横琴金投跟投。创始人邹建发告诉36氪,资金将用于新产品猎户星空推出消杀机器人,致力于做真有用机器人产品来源经济日报新闻客户端日前,北京猎户星空科技有限公司(以下简称猎户星空)正式发布消杀机器人,采用超声波喷雾原理,通过简单操作即可实现全场景广域无死角消杀,还可以根据需求进行个性化场快手CEO程一笑坚持普惠原则,中腰及长尾创作者贡献平台80以上视频播放3月29日,快手科技发布2021年四季度及全年业绩。数据显示,快手四季度DAU(平均日活跃用户)达3。23亿,同比增长19。2,MAU(平均月活跃用户)同比增长21。5达5。78亿荣耀出手了,一亿像素100W快充,颜值产品跌至2219元现在的国内性价比手机市场,几乎所有的手机品牌都不会亲自下场来做了,因为想要提高自己的手机品牌定位,肯定就不可能只做性价比产品,所有很多手机产品都选择分出一个子品牌来做性价比产品,就荣耀Note10的用户狂喜终于等来了新的巨屏旗舰荣耀Note10大概是从2018年起市场上唯一的旗舰巨屏手机了,当年的这款机型搭载了麒麟970处理器超强的对称扬声器在当时已经是很大的5000毫安电池,甚至可以说这款机型为当年的荣阿斯麦CEO回应不卖EUV光刻机给中国大陆苹果股价连涨11天创十多年来最长纪录三星宣布与西部数据合作(全球TMT2022年3月30日讯)今日焦点阿斯麦CEO回应不卖EUV光刻机给中国大陆苹果股价连涨11天创十多年来最长纪录三星宣布与西部数据合作谷歌在法国被罚200万欧元。阿斯麦C中国移动擅自更改用户套餐,投诉无门我该怎么办?被改了套餐(当然是低档改高档)!我用移动28元的套餐近十年了,一直按时缴费,没出过什么问题。去年12月中旬宊然收到短信提示欠费40余元!去营业室咨询了一下,说是套餐由每月28元改成