Java并发编程中,阻塞队列还是很重要的,今天看了ArrayBlockingQueue的源码,有一点小疑惑
阻塞队列添加元素的方法主要有 put, offer和 add,通过变量putIndex控制插入元素的位置,主要看put方法
先获取锁,如果队列不是满的情况下,执行enqueue()插入元素
插入元素后,有一个判断若是下一个插入元素的位置超过数组界限,则重新从数组头开始插入元素。这样不会覆盖以前的数据吗?留着疑问,先看获取元素。
阻塞队列获取元素的方法主要有 take, poll, peek or remove,通过变量takeIndex控制获取元素的位置,主要看take方法
先获取锁,判断队列不为空的情况下,执行dequeue方法
获取元素后,有一个判断若是下一个获取元素的位置超过数组界限,则重新从数组头开始获取元素。所以设计者设置了两个标志位takeIndex,putIndex以及两个condition,是可以保证存放的位置是已经没有元素或者说那个元素已经被取出去了。
ArrayBlockingQueue入队列和出队列不是按照先进先出的,而是通过两个标志位来确定。所以说ArrayBlockingQueue默认情况下不能保证线程访问队列的公平性。
对于之前的疑问,找到ArrayBlockingQueue的构造函数。在构造函数中当下一个插入元素的位置超过数组界限,则重新从数组头开始插入元素,所以ArrayBlockingQueue就是会存在这样的问题,如果这样那就与他的介绍队头元素时队列中存在时间最长的数据元素,而队尾数据则是当前队列最新的数据元素相矛盾了。除非他不是按照数组对应位置判断是不是队列头尾,而是依据takeIndex,putIndex,putIndex位置是队尾。(jdk版本是1.8.0_201)
为此,特地写了代码进行测试,源码很简单
debug代码发现元素确实放在了数组低位置,this对象经过神秘操作调整了顺序确保队头元素时队列中存在时间最长的数据元素的约束