ps:在下文中分别以Array表明ArrayBlockingQueue,Link表明LinkedBlockingQueue,下文中再也不说明。java
Array和Link在并发场景中常用,他们的共同做用就是实现线程安全队列。下面对这两种队列的实现进行对比分析。数组
int i = takeIndex; ... if (++i == items.length) i = 0; ...
这几行在代码在Array中几乎每一个函数都会用到。意思不论是在读取元素,或者存放元素,若是到达数组的最后一个元素,直接将索引移动到第一个位置。你可能会想,若是我一直往队列中添加元素而不取,添加的元素个数超过了数组长度,会不会覆盖以前添加的元素。在实际使用过程当中是不会出现这种状况的,其内部使用了ReentrantLock的Condition,这部分在并发支持中介绍。安全
最大的区别就是Array内部只有一把锁,offer和take使用同一把锁,而Link的offer和take使用不一样的锁。数据结构
在作具体分析以前,先介绍一下ReentrantLock 和其Condition之间的关系。ReentrantLock内部维护了一个双向链表,链表上的每一个节点都会保存一个线程,锁在双向链表的头部自选,取出线程执行。而Condition内部一样维持着一个双向链表,可是其向链表中添加元素(await)和从链表中移除(signal)元素没有像ReentrantLock那样,保证线程安全,因此在调用Condition的await()和signal()方法时,须要在lock.lock()和lock.unlock()之间以保证线程的安全。在调用Condition的signal时,它从本身的双向链表中取出一个节点放到了ReentrantLock的双向链表中,因此在具体的运行过程当中无论ReentrantLock new 了几个Condition其实内部公用的一把锁。介绍完这个以后,我么来分析ArrayBlockingQueue和LinkedBlockingQueue的内部实现不一样。并发
先看其内部锁的定义:函数
int count; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition();
根据上面分析ReentrantLock和其Condition的关系,能够看到放元素和取元素用的同一把锁,没法使放元素和取元素同时进行,只能前后相继执行。性能
内部锁定义:线程
/** Current number of elements */ private final AtomicInteger count = new AtomicInteger(); /** Lock held by take, poll, etc */ private final ReentrantLock takeLock = new ReentrantLock(); /** Wait queue for waiting takes */ private final Condition notEmpty = takeLock.newCondition(); /** Lock held by put, offer, etc */ private final ReentrantLock putLock = new ReentrantLock(); /** Wait queue for waiting puts */ private final Condition notFull = putLock.newCondition();
经过这种设置,能够将在链表头上放元素和在链表尾部取元素再也不竞争锁,在必定程度上能够加快数据处理。指针