您的位置 首页 > 德语词汇

blocking是什么意思,blocking的意思翻译、用法、同义词、(深入浅出阻塞队列BlockingQueue及其典型实现ArrayBlockingQueue)

大家好,今天小编来为大家解答以下的问题,关于blocking是什么意思,blocking的意思翻译、用法、同义词、,深入浅出阻塞队列BlockingQueue及其典型实现ArrayBlockingQueue这个很多人还不知道,现在让我们一起来看看吧!

1、在前面的三篇文章中,我们一起陆续地研究了AQS的底层原理,同时研究了AQS在不同场景下的三个应用工具类(ReentrantLock、CountDownLatch、Semaphore)的工作原理,之所以和大家一起分析它们,是因为这三个类是我们平时工作中应用最多的类了,其实在J.U.C中,还有好多直接或者间接通过AQS实现的工具类,比如读写锁ReentrantReadWriteLock、循环栅栏CyclicBarrier等。

2、本篇我们将一起对AQS的另一类的应用场景的实现类——阻塞队列(实现生产者/消费者模型的经典方法)做一次深入浅出的分析。

blocking是什么意思,blocking的意思翻译、用法、同义词、(深入浅出阻塞队列BlockingQueue及其典型实现ArrayBlockingQueue)

3、从上面的类图结构和源码的注释分析来看,我总结如下:

4、从上面总结的第一和第二点我们知道,BlockingQueue的接口方法包含四种类型的:会抛异常的、返回特殊值的、会阻塞的和阻塞有超时的;但是按照操作结果大体可分为三种(官方区分的):插入型的、删除型的、读取型的。如下面的表格总结(其实也是源码的注释内容):

5、下面我们就此表格说明下(我又多分了一个种类->读取删除型,这样更清晰些):

6、ArrayBlockingQueue是一个通过数组实现的有界阻塞队列。通过FIFO先进先出的方式操作元素,即队头的元素在队列中停留的时间最长,队尾则最短。新元素以尾插入的形式入队,读取将从队头开始。

7、ArrayBlockingQueue可以作为一个“有界缓冲区”,是实现生产者/消费者场景的重要手段。生产者生产元素放入队列,消费者从队列读取进行消费。队列满的时候,生产者就不能继续往里面放入元素,则必须阻塞等待;同样的,当队列为空时,消费者就取不到元素而阻塞等待。ArrayBlockingQueue一旦在创建时初始化了容量大小,就不会变化了(如果随时扩容/减少怎么可以阻塞等待呢)。

8、同时,ArrayBlockingQueue可以支持生产消费过程的是否公平。默认情况是非公平的,当然你也可以自己设置。至于公平和非公平的优缺点这里就不叙述了,看这里。

9、LinkedBlockingQueue从名字就可以看出,它是一个以链表形式实现的队列,它同样是个有界阻塞队列,可以在初始化的时候指定大小,如果不指定,则默认为Integer.MAX_VALUE。相比较ArrayBlockingQueue而言,LinkedBlockingQueue的吞吐量表现更好点,除了这个其他特性几乎和ArrayBlockingQueue差不多。

10、BlockingDeque继承于BlockingQueue,是一个阻塞有界双端队列,即队列的头和尾都可以进行插入,删除,读取操作。同样的,我贴出官方的表格:(细节就不再叙述,其实都差不多,从方法名字也可以看出来是干嘛的了)

11、和BlockingQueue的主要区别就在于,BlockingDeque是两头都可以操作

12、LinkedBlockingDeque就是对BlockingDeque的具体实现,基于链表形式的实现。其具体实现的理念和LinkedBlockingQueue也差不多,也不再叙述了。

13、PriorityBlockingQueue从名字来看,是一个跟优先级有关系的阻塞队列,准确的说,它是一个支持优先级的无界阻塞队列,其内部定义了Comparator属性,我们可以通过这个自定义元素的排列顺序而改变出队的顺序,从而实现优先级,观其构造器就可知一二:

14、publicPriorityBlockingQueue(intinitialCapacity,\nComparator<?superE>comparator){\nif(initialCapacity<1)\nthrownewIllegalArgumentException();\nthis.lock=newReentrantLock();\nthis.notEmpty=lock.newCondition();\nthis.comparator=comparator;\nthis.queue=newObject[initialCapacity];\n}\n

由于是无界,所以使用时小心资源耗尽,发生OOM。

15、Synchronous从英文翻译来看很有意思,同步的队列。乍一看,不懂。。。。源码注释里写道:这是一个阻塞队列,它的每个插入操作必须等待另一个线程执行相应的删除操作,反之亦然。你无法查看同步队列,因为只有当你试图删除一个元素时,它才会出现;你不能插入一个元素(使用任何方法),除非另一个线程试图删除它;你不能迭代,因为没有东西可以迭代。队列的头部是第一个插入队列的线程试图添加到队列中的元素;如果没有这样的队列线程,那么就没有可以删除的元素,poll()将返回null。

16、感觉有点绕,简单来说,该队列无法对元素数据进行存储,无法查看队列里的情况,也无法进行元素数据的迭代,原因就是,一个线程插入完成,就会被另一个线程取走;一个线程取走了,又会再有一个线程继续插入。顾名思义,其实这个队列的数据,感觉像是同步的,插入了就会被取走,取走后就会被再次再次插入。(除非是非常非常巧合的情况下说不定会查看到队列的元素数据)

17、TransferQueue除了继承了BlockingQueue的方法之外,还补充了符合该类特性的方法->Transfer:转移传输的意思,具体哪些呢?如下图接口定义:

18、从源码贴图可知,主要多了上面几个方法:

19、LinkedTransferQueue就是TransferQueue的具体实现,是基于链表形式的无界传输阻塞队列,符合FIFO特性,除了以上介绍的transfer特殊属性外,其他特性和其他实现类差不多。

20、DelayQueue:一个用来存放延迟元素的无界阻塞队列,这个队列不知道大家有没有使用过,它是实现延迟队列的重要手段之一。其里面维护的元素数据是一个实现Delayed接口的自定义实现类的对象,这个类会实现一个方法【longgetDelay(TimeUnitunit)】,如果该方法返回一个小于或等于0的值,就表示里面的元素已经到达设定的延迟时间,这时就会把元素放入到队列当中。具体原理我会在后面专门写一篇文章来阐述。

21、阻塞队列BlockingQueue及其所有实现类的简单介绍到此结束,下面我们将从上面的具体实现类选取ArrayBlockingQueue作为本篇的另一个主角,来看看具体的阻塞队列是如何实现的。

22、(PS:很重要的一点,注意每个队列的名字,有界和无界,使用无界队列的时候一定要小心OOM)

23、从上面分析阻塞队列以及ArrayBlockingQueue的简单介绍来看,ArrayBlockingQueue最重要的功能就是,能够在操作元素(插入和读取删除)的时候阻塞。其工作原理就是,当ArrayBlockingQueue队列为空时,会阻塞消费者读取数据,直到队列有数据时唤醒消费者继续;当队列满的时候,会阻塞生产者进行插入数据,直到队列有剩余空间时,会唤醒生产者继续插入。

24、在之前的篇章中,我们就已经知道,wait/notify是实现生产者/消费者的重要手段之一,是通过线程间的通信来完成的,那在我们J.U.C包里,会不会是通过Condition的等待队列机制实现的呢?我们一起来看下,上源码:

25、/**Thequeueditems*/\nfinalObject[]items;\n\n/**itemsindexfornexttake,poll,peekorremove*/\ninttakeIndex;\n\n/**itemsindexfornextput,offer,oradd*/\nintputIndex;\n\n/**Numberofelementsinthequeue*/\nintcount;\n\n/*\n*Concurrencycontrolusestheclassictwo-conditionalgorithm\n*foundinanytextbook.\n*/\n\n/**Mainlockguardingallaccess*/\nfinalReentrantLocklock;\n\n/**Conditionforwaitingtakes*/\nprivatefinalConditionnotEmpty;\n\n/**Conditionforwaitingputs*/\nprivatefinalConditionnotFull;\n

从这段代码其实我们就已经知道了上面问题的答案,确实是通过Condition的等待队列机制实现的。

26、我们再来看看构造器,一共重载实现了三个构造器,如下:

27、publicArrayBlockingQueue(intcapacity){\nthis(capacity,false);\n}\n\npublicArrayBlockingQueue(intcapacity,booleanfair){\nif(capacity<=0)\nthrownewIllegalArgumentException();\nthis.items=newObject[capacity];\nlock=newReentrantLock(fair);\nnotEmpty=lock.newCondition();\nnotFull=lock.newCondition();\n}\n\npublicArrayBlockingQueue(intcapacity,booleanfair,\nCollection<?extendsE>c){\nthis(capacity,fair);\n\nfinalReentrantLocklock=this.lock;\nlock.lock();//Lockonlyforvisibility,notmutualexclusion\ntry{\ninti=0;\ntry{\nfor(Ee:c){\ncheckNotNull(e);\nitems[i++]=e;\n}\n}catch(ArrayIndexOutOfBoundsExceptionex){\nthrownewIllegalArgumentException();\n}\ncount=i;\nputIndex=(i==capacity)?0:i;\n}finally{\nlock.unlock();\n}\n}\n

从构造器可以看出,ArrayBlockingQueue公平/非公平都支持,默认使用非公平模式。初始化的时候,就已经将消费/生产两大条件等待队列初始化完成了

28、publicvoidput(Ee)throwsInterruptedException{\ncheckNotNull(e);\n/**\n为了保证操作的原子性,上来先上锁\n*/\nfinalReentrantLocklock=this.lock;\nlock.lockInterruptibly();\ntry{\n/**\n当前队列元素实际个数count如果等于数组定义的大小的话,则说明队列里已经满了\n则会调用生产者等待队列的阻塞await方法,对当前消费者线程进行阻塞\n注意:这边是个while循环,跳出循环的唯一条件就是,count!=items.length,什么情况才会\n不相等呢?当然是队列中的元素被读取删除的时候\n*/\nwhile(count==items.length)\nnotFull.await();\n/**\n如果不满足上面的条件或者生产者被唤醒从阻塞状态回归时,将调用enqueue(e)方法进行入队操作。\n*/\nenqueue(e);\n}finally{\nlock.unlock();\n}\n}\n\nprivatevoidenqueue(Ex){\n/**\n这部分的代码就很简单了\n首先,就是将当前需要入队的元素,放入putIndex下标对应的坑中\n其次,将putIndex执行+1操作,如果+1后等于数组的长度,说明下标走到尽头了,得重新开始,于是执行=0操作\n最后,以上操作都成功的话,将元素个数count执行+1;并且调用消费者等待队列,唤醒一个阻塞的消费者进行\n消费\n*/\n//assertlock.getHoldCount()==1;\n//assertitems[putIndex]==null;\nfinalObject[]items=this.items;\nitems[putIndex]=x;\nif(++putIndex==items.length)\nputIndex=0;\ncount++;\nnotEmpty.signal();\n}\n

流程比较简单,看代码注释即可明白啦~~

29、publicEtake()throwsInterruptedException{\n/**\n同样的,进入方法先上锁,保证原子性操作,其实也不仅仅是为了保证原子性\n我们在之前讲过Condition的原理的时候说过,调用其方法前必须先lock\n*/\nfinalReentrantLocklock=this.lock;\nlock.lockInterruptibly();\ntry{\n/**\n几乎和上面一样,首先是个while循环,条件是当前元素的个数是否为0\n如果为0,说明队列里没数据可以读取消费,则执行notEmpty.await()对消费者阻塞\n如果不为0或者从阻塞等待队列中被唤醒,则执行出队操作dequeue()\n*/\nwhile(count==0)\nnotEmpty.await();\nreturndequeue();\n}finally{\nlock.unlock();\n}\n}\n\nprivateEdequeue(){\n/**\n首先,取出当前takeIndex下标对应的元素,并且置为空\n其次,takeIndex执行+1操作,并判断是否等于数组大小,判断和上面一样哈~\n最后,count进行-1操作,因为出去了一个元素;并且调用生产者继续放入元素\n*/\n//assertlock.getHoldCount()==1;\n//assertitems[takeIndex]!=null;\nfinalObject[]items=this.items;\n@SuppressWarnings("unchecked")\nEx=(E)items[takeIndex];\nitems[takeIndex]=null;\nif(++takeIndex==items.length)\ntakeIndex=0;\ncount--;\nif(itrs!=null)\nitrs.elementDequeued();\nnotFull.signal();\nreturnx;\n}\n

同样的,代码注释里,已经分析清楚了~~至于其他的一些操作方法小伙伴们可以自己去看哈~也比较简单。

30、到此,我们想要了解的都理清楚了,简单总结下:

31、有了之前AQS的基础,理解阻塞队列ArrayBlockingQueue的原理是不是轻松加愉快呢~当然我们后面也会选一些其他的实现类来分析的,有的可能就比ArrayBlockingQueue复杂多了。

32、下面我们写个简单的示例,来加深下对阻塞队列ArrayBlockingQueue的理解。

33、场景描述:一个队列大小为5的阻塞队列,多个生产者往队列放入随机号,一个消费者进行读取消费。

34、publicclassArrayBlockingQueueDemo{\n\tpublicstaticvoidmain(String[]args){\n\t\tArrayBlockingQueue<String>queue=newArrayBlockingQueue<>(5,true);\n//先把消费者搞起来\n\t\tThreadconsumer=newThread(newRunnable(){\n@Override\npublicvoidrun(){\n\t\t\ttry{\n\t\t\t\tfor(;;){\n\t\t\t\t\tStringvalue=queue.take();\n\t\t\t\t\tSystem.out.println("消费者取出了值"+value);\n\t\t\t\t}\n\t\t\t}catch(InterruptedExceptione){\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n},"consumer");\n\t\tconsumer.start();\n//循环启动10个生产者\n\t\tfor(inti=0;i<10;i++){\nThreadproducer=newThread(newRunnable(){\n\t\t\t@Override\n\t\t\tpublicvoidrun(){\ntry{\n\t\t\t\tStringvalue=UUID.randomUUID().toString();\n\t\t\t\tSystem.out.println(Thread.currentThread().getName()+"放入值:"+value);\n\t\t\t\t\tqueue.put(value);\n}catch(InterruptedExceptione){\n\t\t\t\te.printStackTrace();\n}\n\t\t\t}\n},"producer"+i);\nproducer.start();\n\t\t}\n\t}\n}\n\n\n/*运行效果\nproducer1放入值:e06ee09f-46c0-4c33-92b3-53b4300fc1af\nproducer5放入值:03ed5819-6b51-4ef9-bb93-5f048ead0a1e\nproducer4放入值:9988b80b-7218-4b95-bbe0-e6d8ec00ba38\n消费者取出了值e06ee09f-46c0-4c33-92b3-53b4300fc1af\nproducer3放入值:01fc2d1c-6fc5-4968-b5ef-dde07639bde4\nproducer8放入值:410903e5-4e4d-4310-a84d-b87f94f4a797\nproducer0放入值:3323dda4-baf4-4b27-b574-95cda9ce0b14\nproducer7放入值:13fc6650-69ff-433e-bdd3-4e726d41abdf\nproducer2放入值:e398813a-8b50-4221-8a13-845ae91a453a\nproducer6放入值:9696211c-f2bf-466a-bebc-b55de4608108\nproducer9放入值:f106cffa-351e-4d4e-ad1a-6986f7136c3a\n消费者取出了值03ed5819-6b51-4ef9-bb93-5f048ead0a1e\n消费者取出了值9988b80b-7218-4b95-bbe0-e6d8ec00ba38\n消费者取出了值01fc2d1c-6fc5-4968-b5ef-dde07639bde4\n消费者取出了值410903e5-4e4d-4310-a84d-b87f94f4a797\n消费者取出了值3323dda4-baf4-4b27-b574-95cda9ce0b14\n消费者取出了值13fc6650-69ff-433e-bdd3-4e726d41abdf\n消费者取出了值e398813a-8b50-4221-8a13-845ae91a453a\n消费者取出了值9696211c-f2bf-466a-bebc-b55de4608108\n消费者取出了值f106cffa-351e-4d4e-ad1a-6986f7136c3a\n\n当然效果每次运行都会不一样的,你可能会有疑问,不是生产一个消费一个吗?\n你错了,我们模拟的是多个生产者,只有一个消费者,当消费者在消费打印的时候,说不定已经生产好几个了。\n请注意,这是一个高并发场景。\n*/\n

由此我们看出,只有生产者生产放入数据后,消费者才会进行读取消费

35、到此,本篇到此结束啦~你学会了吗?~~

36、作者:会飞的鱼2022链接:https://juejin.cn/post/7117889476709318669

文章分享结束,blocking是什么意思,blocking的意思翻译、用法、同义词、和深入浅出阻塞队列BlockingQueue及其典型实现ArrayBlockingQueue的答案你都知道了吗?欢迎再次光临本站哦!

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023