推荐公众号
ArrayBlockingQueue
数组,ReentranLock,Condition实现,必须指定数组大小。其实很好分析,读写都必须获取锁。利用两个condition来实现,当队列为空时,如果读线程来那元素,就让读线程等待(Condition.await());如果时写线程就向数组中添加元素,对应的下一次写操作元素位置等数组操作,并且执行notEmput.signal()方法。当队列满的情况下,如果读线程来拿元素,就把
下一次读操作位置等数组操作,执行notFull.signal()。
工作机制
[外链图片转存失败(img-vFzKlJNh-1565056308759)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
属性/操作
//存放队列元素的数组
final Object[] items;
//下一次读操作的位置
int takeIndex;
//下一次写操作的位置
int putIndex;
//队列中的总元素
int count;
/*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/
//锁
final ReentrantLock lock;
//
private final Condition notEmpty;
//
private final Condition notFull;
构造器
//重载的有好几个构造器 最终会调用这个构造器
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
读操作
take, poll, peek or remove
写操作
put, offer, or add
其实就是数组操作+conditioin,不进行源码分析,不懂请看condition介绍
LinkedBlockingQueue
单向链表实现.读锁,写锁是分开的
工作机制
[外链图片转存失败(img-4pg3w4cP-1565056308761)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
属性/操作
链表容量
/** The capacity bound, or Integer.MAX_VALUE if none */
private final int capacity;
//链表中的元素数量
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();
//链表中的头节点
/**
* Head of linked list.
* Invariant: head.item == null
*/
transient Node<E> head;
//链表中的尾节点
/**
* Tail of linked list.
* Invariant: last.next == null
*/
private transient Node<E> last;
//“读锁”
/** 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();
构造器
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
//所谓的无界队列构造器
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
节点Node
/**
* Linked list node class
比较简单只有一个后继
*/
static class Node<E> {
E item;
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head.next
* - null, meaning there is no successor (this is the last node)
*/
Node<E> next;
Node(E x) { item = x; }
}
写操作
以put为例
位置:LinkedBlocking 331
方法目的:入队
方法流程:
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
//这里为什么初始化为-1 可以看看offer方法标识是否入队成功
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
//如果是满得 写线程等待
while (count.get() == capacity) {
notFull.await();
}
//链表操作 将node作为last
enqueue(node);
//队列中元素数量 + 1 注意c是+1之前的数
c = count.getAndIncrement();
//如果 c + 1还比容量小 唤醒写线程
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
//如果入队之前 是空的 那么如果有读线程的话一定是挂起的 这里唤醒一下
if (c == 0)
signalNotEmpty();
}
读操作
以take为例
位置:LinkedBlocking 434
方法目的:入队
方法流程:
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
//如果为空队列 等待
while (count.get() == 0) {
notEmpty.await();
}
//出队 链表操作
x = dequeue();
//队列中元素数 - 1
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
//同样这里如果 出队之前是满队列 那么这里需要唤醒 写线程
if (c == capacity)
signalNotFull();
return x;
}