一)LinkedBlockingQueue<E>簡介
LinkedBlockingQueue<E>源碼詳解(基於jdk1.8.0_162)
1)底層由單鏈表實現。
2)是一個FIFO(先進先出)的阻塞隊列。
3)創建時,可指定隊列初始容量,如未指定,默認爲Integer.MAX_VALUE,指定後不能修改。是一個線程安全的隊列。
類圖:LinkedBlockingQueue<E>繼承了AbstractQueue<E>類並實現了BlockingQueue<E>接口。
二)構造方法
1)LinkedBlockingQueue()
創建一個 LinkedBlockingQueue ,容量爲 Integer.MAX_VALUE 。
2)LinkedBlockingQueue(int capacity)
創建一個具有給定(固定)容量的 LinkedBlockingQueue 。
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); // 把隊列頭尾相連並初始化爲null
}
三)添加
1)void put(E e)
在該隊列的尾部插入指定的元素,如果需要,等待空格變爲可用。
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException(); // 元素不能爲null
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.await();
}
enqueue(node); // 在隊列尾部添加新節點
c = count.getAndIncrement(); // 隊列數量累計1
if (c + 1 < capacity) // 如果隊列實際容量小於固定容量,喚醒生產者線程
notFull.signal();
} finally {
putLock.unlock(); // 解鎖
}
if (c == 0)
signalNotEmpty(); // 喚醒消費者線程
}
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node; // 把新節點添加在隊列尾部
}
四)查找
1)boolean contains(Object o)
如果此隊列包含指定的元素並存在多個,查找到第一個元素之後就返回 true,否則返回false。
public boolean contains(Object o) {
if (o == null) return false; // 對象爲空, 返回false
fullyLock(); // 加鎖
try {
for (Node<E> p = head.next; p != null; p = p.next) // 從頭節點開始循環查找元素
if (o.equals(p.item)) // 查找到第一個元素,直接返回true
return true;
return false; // 未找到元素, 返回false
} finally {
fullyUnlock(); // 解鎖
}
}
2)E peek()
檢索但不刪除此隊列的頭,如果此隊列爲空,則返回 null 。
public E peek() {
if (count.get() == 0) // 如果隊列爲空, 則返回null
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
Node<E> first = head.next; // 獲取頭部節點
if (first == null) // 如果頭部節點爲null, 則返回null
return null;
else
return first.item; // 返回頭部節點的item
} finally {
takeLock.unlock();
}
}
3)E poll()
檢索並刪除此隊列的頭,如果此隊列爲空,則返回 null 。
public E poll() {
final AtomicInteger count = this.count; // 獲取隊列數量
if (count.get() == 0) // 隊列爲空, 返回null
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) { // 隊列不爲空, 刪除隊列頭部元素
x = dequeue();
c = count.getAndDecrement(); // 隊列數量累減1
if (c > 1) // 如果隊列數量大於1, 喚醒消費者線程
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull(); // 喚醒生產者線程
return x; // 返回的元素信息
}
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; // 返回原頭部節點信息
}
4)E take()
檢索並刪除此隊列的頭,如有必要,等待元素可用。如隊列沒有元素,會一直阻塞。
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) { // 如果隊列爲null,則一直阻塞,等待生產者生產元素,再喚醒消費者線程
notEmpty.await();
}
x = dequeue(); // 獲取隊列頭部節點
c = count.getAndDecrement(); // 隊列數量累減1
if (c > 1)
notEmpty.signal(); // 當隊列數量大於1時,喚醒消費者線程
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull(); // 喚醒生產者線程
return x;
}
五)刪除
1)void clear()
從這個隊列中原子地刪除所有的元素。
public void clear() {
fullyLock();
try {
for (Node<E> p, h = head; (p = h.next) != null; h = p) { // 把隊列元素循環置null
h.next = h;
p.item = null;
}
head = last; // 把頭部節點和尾部節點相連
// assert head.item == null && head.next == null;
if (count.getAndSet(0) == capacity) // 把隊列數量初始化爲0
notFull.signal(); // 喚醒生產者線程
} finally {
fullyUnlock();
}
}
2)boolean remove(Object o)
從該隊列中刪除指定元素的單個實例(如果存在)。
public boolean remove(Object o) {
if (o == null) return false;
fullyLock();
try {
for (Node<E> trail = head, p = trail.next;
p != null; trail = p, p = p.next) {
if (o.equals(p.item)) { // 從頭部節點開始,循環比較元素,如找到元素,先刪除,再返回true
unlink(p, trail);
return true;
}
}
return false; // 未找到元素,返回false
} finally {
fullyUnlock();
}
}
void unlink(Node<E> p, Node<E> trail) {
// assert isFullyLocked();
// p.next is not changed, to allow iterators that are
// traversing p to maintain their weak-consistency guarantee.
p.item = null; // 把找到的元素置null
trail.next = p.next; // 臨時存儲刪除元素的下一節點
if (last == p) // 如果刪除元素是尾部元素
last = trail; // 重新指定尾部元素
if (count.getAndDecrement() == capacity) // 隊列數量累減1
notFull.signal(); // 喚醒生產者線程
}
識別二維碼關注個人微信公衆號
本章完結,待續,歡迎轉載!
本文說明:該文章屬於原創,如需轉載,請標明文章轉載來源!