1、BlockingQueue
BlockingQueue为阻塞队列,其与普通队列不同的是,以put和get为例,阻塞队列在put时,若列表满了,则会等待直到队列可以加入元素;而阻塞队列在get时候,若列表为空,则会等待到队列非空。可以用来解决 生产者消费者问题。
2、ArrayBlockingQueue
阻塞队列在add时,若列表满了,则抛出异常;
阻塞队列在offer时根据不同参数决定;
阻塞队列在put时,若列表满了,则会等待直到队列可以加入元素;
阻塞队列在get时候,若列表为空,则会等待到队列非空。
特点:通过一个lock进行实现,存取共用一个锁,并且是先进先出FIFO;
源码中该阻塞队列提供了3个构造函数:
// 通过指定队列的大小
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
// 队列大小 以及 是否创建公平锁 true为公平锁 false为非公平锁
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();
}
// 将队列c元素都加入初始化的队列中
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
lock.lock(); // Lock only for visibility, not mutual exclusion
try {
int i = 0;
try {
for (E e : c) {
checkNotNull(e);
items[i++] = e;
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalArgumentException();
}
count = i;
putIndex = (i == capacity) ? 0 : i;
} finally {
lock.unlock();
}
}
从上面可以了解到,ArrayBlockQueue是指定大小的,并且是通过ReentrantLock(实现公平锁和非公平锁)来进行锁定
ArrayBlockQueue的add(e)实际就是调用了offer(e)
// 根据代码就是获取到ReentranLock 通过加锁和解锁 来实现
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
// 加入到队列中,putIndex 在每次执行完后都回加一,即表示下次放入数组中的元素
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
// 标记表示该队列非空,等待中的消费者可以进行执行
notEmpty.signal();
}
// offer中有一个方法为在指定时间内,若队列满则等等
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
// put则为一直等待 知道队列非满
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
阻塞队列的take
// 从阻塞队列中获取元素
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 获取锁,若中断则获取 数据失败
lock.lockInterruptibly();
try {
// 若没有元素则等待 则到notEmpty条件被清除notEmpty.signal();
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
// 从队列中取出元素takeIndex 与putIndex类似初始值为0,遍历完一次take后从0从新开始,遵循先进先出
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();//同时更新迭代器中的元素数据
// 置为非满队列
notFull.signal();
return x;
}
3、LinkedBlockingQueue
private final ReentrantLock putLock = new ReentrantLock();写入元素的锁指定为非公平锁
/** Wait queue for waiting puts */ private final Condition notFull = putLock.newCondition();
private final ReentrantLock takeLock = new ReentrantLock(); 取出元素的锁,被指定为非公平锁。
/** Wait queue for waiting takes */ private final Condition notEmpty = takeLock.newCondition();
特点:通过存取锁分别用两个锁,都为非公平锁,(可以有效的防止共用一个锁时,一直被写锁lock而读锁一直得不到锁的情况)文中以读锁和写锁相称:
同样通过构造器查看:
// 生成一个没有大小限制的链表
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
// 指定大小的链表
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
// 将其他队列中的元素加入该链表
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
putLock.lock(); // Never contended, but necessary for visibility
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
enqueue(new Node<E>(e));
++n;
}
count.set(n);
} finally {
putLock.unlock();
}
}
加入元素的方法:
// 加入元素
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;//判断是否为指定容器大小,使用原子包装,这样可以获取实施的count,防止被take了这里却还未减少数量
if (count.get() == capacity) // 若等于容器则返回false
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;// 获取写锁并进行锁定
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
// 加入队列只需要使其下一个节点设值
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
// 获取读锁,并将非空条件去除
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
// put方法
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.
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();//若满则释放写锁,并等待
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
取出元素:
// 取出元素 锁定读锁,
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();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
// 标记为队列非满状态
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}