ArrayBlockingQueue源码解析

时间:2022-07-23
本文章向大家介绍ArrayBlockingQueue源码解析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

好久没有写源码分析了,对于像我一样的赖人来说,坚持真的比较难,如果对自己做个评价,我会说“间歇性踌躇满志,持久性混吃等死”。这次我又良心发现,继续写写文档,在动笔之前,我还在想我学过的东西,能不能用出来呐?这个问题挺严重的。如果把学习到东西不能用出来,那等价与啥也不会,虽然也能在心中种下一颗种子,但至于种子何时发芽的事。我觉得就看个人了,为了避免这种情况,还是的在分析源码的时候联系现实。而不能像是在看大戏。毕竟娱乐致死不无道理。

今天继续分析我们JUC包,ArrayBlockingQueue显然根据名称我们就知道此类是什么,首先它的核心应该是一个数组,然后还是阻塞的,然后连起来就是使用数组做的阻塞队列了。还是按照我们一般的套路,先想想一下它的实现机制吧,就是咱们自己如何实现这玩意。我们知道并发的时候出问题一般都是写操作,读操作并没有什么问题,这里说的没有问题是我们一定要采用volatile修饰,那么如果是一种对象列表的话,我们可以使用JUC提供的乐观锁,这样也可以很好的实现这个问题。那么ArrayBlockingQueue阻塞的到底是什么呐?在我看来无非就是让写入的线程排队,让读取的线程进行CAS判断时候预期的元素还存在的问题。那么我们还是看看源码是怎么长得。

发现这个类的方法挺多,然后还有自己的迭代器,由于这里都要考虑多线程的情况,那么估计这种自己实现迭代器的原理基本类似并可能会比较常见。

显然此类实现了BlockingQueue接口,而BlockingQueue也就是定义了一套规范,这套规范就是对所有的队列都有用,然后就避免个子为营,然后乱乱的感觉。

说这些咱们脑子里也是空空如也,不知此类的用处。

我们在使用数组的时候,一般的操作就是创建数组了,那么我们就从队列初始化开始吧。

这里我们可以传入初始化的队列大小,是否公平锁,和队列的参数

我们发现我们的客人居然就在我们Object数组中,然后再根据传递的参数初始化一个公平锁或者非公平锁,默认采用非公平锁。

如果我们传入的是一个队列,那么ArrayBlockingQueue会先初始化队列和锁,然后对赋值的复制块进行加锁,进行单线程运行。

显然这里的count记录元素的个数,puIndex表示下一次插入的数组下标。

notEmpty = lock.newCondition();
notFull =  lock.newCondition();

至于这两句代码,我还不太理解。之前分析的也特意绕过去了。哈哈但是我跟踪进去发现,啥也没有。可能在那个子类中实现了。这里先不管了,看后边会不会发生思维的自锁情况。

在添加元素的时候,调用了父类的add方法,但是父类调用了子类的offer方法

。一路跟踪到queue接口,发现最后还是调的子类的offer方法。感觉饶了一圈,好像没什么意义。

在offer方法中,发现当count==items.length的时候就直接不让入队列了。否则

将加入的元素放到putIndex中,如果putIndex和数组长度想等得时候就将插入的下标复位到0

使用put添加元素的时候

如果队列满了,那么就进行阻塞,然后进行aqs进行排队,然后获取到了运行资格还得判断队列是否满了。直到队列没满,然后把元素插入到队列最后。

对队列进行出队操作时

如果是poll方法时。如果队列为空则返回null,否则先获取值,然后置空,然后修改队列的容量和坐标等消息。

使用take获取元素的时候,如果队列中没有值得时候,就会发生阻塞,直到不为空的时候,才会进行获取操作。

在remove方法中,先遍历数组寻找与当前节点值相同的节点,然后调用迭代器就行数据的删除。

总结:

ArrayBlockingQueue阻塞队列,使用takeIndex表示出队下标,使用putIndex作为入队队列,先进先出。在对队列的写操作的时候都进行加锁处理。take()方法和put()方法是对应的,从中拿一个数据,如果拿不到线程挂起。poll()方法和offer()方法是对应的,从中拿一个数据,如果没有直接返回null 。

应用:

ArrayBlockingQueue的使于那种先进先出的情况,比如生产者和消费者的情况。以及一些顺序要求严格的情况。