目錄
結構:
public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { private final int capacity;//queue的容量大小 private final AtomicInteger count = new AtomicInteger(0);//當前存儲的數量 private transient Node<E> head;//Head of linked list. private transient Node<E> last;//Tail of linked list. private final ReentrantLock takeLock = new ReentrantLock();//取出鎖 private final Condition notEmpty = takeLock.newCondition(); /** Wait queue for waiting takes */ private final ReentrantLock putLock = new ReentrantLock();//放入鎖 private final Condition notFull = putLock.newCondition();/** Wait queue for waiting puts */ 原文:https://blog.csdn.net/lan861698789/article/details/81323750 static class Node<E> { E item; Node<E> next; Node(E x) { item = x; } } } |
分析:
結構包含:容量大小、存儲數量大小、頭節點、尾節點、put和take鎖
這裏的Node和linkedlist裏的Node不同,這裏只有next節點,沒有父節點。
擁有兩把鎖,這是take和put不相互影響的關鍵。
原文:https://blog.csdn.net/lan861698789/article/details/81323750
PUT操作
public void put(E e) throws InterruptedException { int c = -1; Node<E> node = new Node(e); final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; putLock.lockInterruptibly(); try { while (count.get() == capacity) {//如果大小和容量一樣了,表示不能放入了,要阻塞。 //While用的比if好多了。萬一消費者GET時候,喚醒了多個。 notFull.await();//當前線程休息,等待signal隨機喚醒。 } last = last.next = node;//賦值新元素的最後一個位置 c = count.getAndIncrement();//大小+1 if (c + 1 < capacity)//如果當前大小還是小於容量,則繼續喚醒一個put線程 notFull.signal(); } finally { putLock.unlock(); } if (c == 0) {//此時隊列中總共有1個node了。因爲初始值是0。Put後會增加getAndIncrement signalNotEmpty();//通知take消費者,可以消費 } } 原文:https://blog.csdn.net/lan861698789/article/details/81323750 private void signalNotEmpty() { final ReentrantLock takeLock = this.takeLock; takeLock.lock(); try { notEmpty.signal(); } finally { takeLock.unlock(); } } |
分析:
流程:
1.判斷是否能生產放入
2.如果不能生產,則釋放當前put鎖,線程阻塞等待。
3.如果能生產,則向隊列尾部新增元素。
注意:
極端情況下當生產者都阻塞等待了,哪裏來喚醒?
可以看take操作,在take後,會判斷取操作之前的大小和容器大小一樣,則signal一個生產者,讓他繼續工作。這個被喚醒的生產者會繼續喚醒其他的生產者。
if (c == capacity) { signalNotFull();//通知一個生產者,可以生產了 } |
原文:https://blog.csdn.net/lan861698789/article/details/81323750
TAKE操作
public E take() throws InterruptedException { E x; int c = -1; final AtomicInteger count = this.count; final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly();//lockInterruptibly()方法是一個可以對中斷進行響應的鎖申請動作 try { while (count.get() == 0) {//如果大小=0,表示不能取了,要阻塞 notEmpty.await(); } //取出頭元素 Node<E> h = head; Node<E> first = h.next; h.next = h; // help GC head = first; x = first.item; first.item = null;
c = count.getAndDecrement();//先賦值,再減1 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(); } } |
分析:
流程:
1.判斷是否能消費取出
2.能,則從隊列尾部取
3.不能,則釋放take鎖,阻塞等待被喚醒。
原文:https://blog.csdn.net/lan861698789/article/details/81323750
注意:
極端情況下當消費者都阻塞等待了,哪裏來喚醒?
可以看put操作,在put後,會判斷取操作之前的大小=0,則signal一個消費者,讓他繼續工作,這個被喚醒的消費着會繼續喚醒其他的消費者。
if (c == 0) { signalNotEmpty();//通知take消費者,可以消費 } |