阻塞队列
LinkedBlockingQueue
- 内部使用Node 节点组成一个单链表
- head 、 last 并且保存了头尾节点、
- takeLock = new ReentrantLock();
- 删除的时候锁
- putLock = new ReentrantLock();
- 存放的时候锁
- notEmpty = takeLock.newCondition();
- 不为空时候的newCondition
- notFull = putLock.newCondition();
- 不满时候的newCondition
- capacity 容量
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
private final int capacity;
private final AtomicInteger count = new AtomicInteger();
transient Node<E> head;
private transient Node<E> last;
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();
}
构造函数
默认的无参调用有参,并且使用 Integer的最大值。
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
直接把 容量设置为 Integer的最大值。并且初始化 队列, 设置头尾节点。
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
初始化之后的结构如下
类中的方法
- 可以看到类中的方法和 ArrayBlockingQueue 中类似
- 都是一些入队出队的方法
- 毕竟上层接口也是 Queue
- 都是一些入队出队的方法
添加元素
add 方法
- 在这里面会调用 offer 方法来做出实现
- 因为它们(LinkedBlockingQueue、ArrayBlockingQueue )是 AbstractQueue 的子类 所以在一些方法的调用流程上都是差不多的,走父类的方法,然后在用自己的具体实现。
- offer 方法 为false的时候,会抛出异常。
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
LinkedBlockingQueue
offer方法的具体实现
-
判断要添加的元素是不是为空
-
然后取出当前队列的大小
- 如果大小已经等于容量(已经满了) 直接 false
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
int c = -1;
// 新建立节点
Node<E> node = new Node<E>(e);
// 得到添加的锁
final ReentrantLock putLock = this.putLock;
// 上锁
putLock.lock();
try {
// 这个条件如果不成立的话,c 的值依旧会是 -1 , 方法会直接判断添加失败
if (count.get() < capacity) {
// 队列没有满, 添加元素
enqueue(node);
// 第一次添加的话, 此时 c = 0
c = count.getAndIncrement();
// 添加完成并且队列不满,调用
if (c + 1 < capacity)
// 没有满
notFull.signal();
}
} finally {
// 解锁
putLock.unlock();
}
// 第一次添加元素的时候, 会调用这个方法
if (c == 0)
signalNotEmpty();
return c >= 0;
}
原子类的操作
private static void testAtomicInteger() {
AtomicInteger integer = new AtomicInteger();
int i = integer.get();
System.out.println("get: " + i);
int andIncrement = integer.getAndIncrement();
System.out.println("getAndIncrement: " + andIncrement);
i = integer.get();
System.out.println("get: " + i);
}
get: 0
getAndIncrement: 0
get: 1
enqueue
- 把node 元素添加到尾节点的后面
- 更新尾节点
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
此时添加一个元素后结构如下
添加两个元素之后的结构如下
删除元素
remove 同 add 方法为公用方法
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
LinkedBlockingQueue
poll 方法的执行过程和 offer 方法类似
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
// 此时队列中不是空的
if (count.get() > 0) {
// 得到元素
x = dequeue();
// 更新 c 的值
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
dequeue
从队头删除了一个节点,并且返回队头的值
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
删除之前阻塞队列的结构如下
新建h \ first 指针后的结构如下
开始删除时候的结构如下
- h.next = h; // help GC 这一行代码, 也帮助了GC
remove(Object o)
public boolean remove(Object o) {
// 判断非空
if (o == null) return false;
// 全部加锁
fullyLock();
/**
* putLock.lock();
* takeLock.lock();
**/
try {
// 直接 for 循环遍历 链表
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (o.equals(p.item)) {
// 使用 unlink(p, trail); 删除
// 传递的是找到的这个节点和它前驱节点
unlink(p, trail);
return true;
}
}
// 到这里队列中没有这个元素
return false;
} finally {
// 全部解锁
fullyUnlock();
}
}
unlink(p, trail)
- 改变前驱节点的指针来删除当前节点
void unlink(Node<E> p, Node<E> trail) {
p.item = null;
trail.next = p.next;
// 如果要删除的是尾部节点, 须更新一下尾部节点
if (last == p)
last = trail;
// 删除之前队列是满的, 此时要通知 其他阻塞线程
if (count.getAndDecrement() == capacity)
notFull.signal();
}
如果要删除 3
trail.next = p.next; 执行完之后
得到元素
peek()
此方法直接返回队首元素
public E peek() {
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
可能会等待的方法
put(E e)
- 如果添加 的时候队列满了
- 不会抛出异常
- 会等待有元素出队后, 再次添加元素
- 不会抛出异常
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// 设置变量C
int c = -1;
// 新建一个节点
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
// 如果当前队列的大小已经满了
while (count.get() == capacity) {
// notFull 这个 Condition会等待
notFull.await();
}
// 有其他的线程弹出了一个元素,开始添加
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
// 添加完这个元素之后, 队列还是不满,就唤醒其他等待的线程
notFull.signal();
} finally {
// 解锁
putLock.unlock();
}
// 这是第一次添加做的特殊判断
if (c == 0)
signalNotEmpty();
}
take()
和上方 put(E e) 方法类似
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;
}
是否包含
- 全部加锁
- 使用 for 循环 + equals 来判断
public boolean contains(Object o) {
if (o == null) return false;
fullyLock();
try {
for (Node<E> p = head.next; p != null; p = p.next)
if (o.equals(p.item))
return true;
return false;
} finally {
fullyUnlock();
}
}
总结
- 这个阻塞队列如果不指定大小的话,可以无限制大
- 使用链表来作为队列的实现
- 使用原子类来存放大小
- AtomicInteger、
- 初始化的时候可以选择是否使用公平锁、非公平锁
- 不可以存放 null
- 是一个线程安全的队列
- 内部使用 Lock来 控制并发