LinkedBlockingQueue的put,take方法
put操作:在LinkedBlockingQueue?中有putlcok和takelock倆把鎖,put操作使用putlock這把鎖,利用lockInterruptibly方法加鎖,該方法的作用為:如果該線程被標記為中斷,可拋出異常。加鎖之后,判斷count是否等于容量,相等的話條用await()方法線線程加到條件隊列中去,直到喚醒,把元素加入到隊列,然后判斷一下count+1如果還沒有達到容量值的話,再次調用notfull的signal方法,因為有可能多個線程阻塞到notfull條件隊列中。最后釋放鎖。
釋放完鎖之后,如果原來count值為0的話,現在加了一個元素,調用signalNotEmpty方法,執行notEmpty.signal(),值得注意的是此過程得用takelock加鎖,原因是防止其他線程消費了。
public void put(E e) throws InterruptedException {// 不允許null元素if (e == null) throw new NullPointerException();int c = -1;// 新建一個節點Node<E> node = new Node<E>(e);final ReentrantLock putLock = this.putLock;final AtomicInteger count = this.count;// 使用put鎖加鎖putLock.lockInterruptibly();try {// 如果隊列滿了,就阻塞在notFull條件上// 等待被其它線程喚醒while (count.get() == capacity) {notFull.await();}// 隊列不滿了,就入隊enqueue(node);// 隊列長度加1c = count.getAndIncrement();// 如果現隊列長度如果小于容量// 就再喚醒一個阻塞在notFull條件上的線程// 這里為啥要喚醒一下呢?// 因為可能有很多線程阻塞在notFull這個條件上的// 而取元素時只有取之前隊列是滿的才會喚醒notFull// 為什么隊列滿的才喚醒notFull呢?// 因為喚醒是需要加putLock的,這是為了減少鎖的次數// 所以,這里索性在放完元素就檢測一下,未滿就喚醒其它notFull上的線程// 說白了,這也是鎖分離帶來的代價if (c + 1 < capacity)notFull.signal();} finally {// 釋放鎖putLock.unlock();}// 如果原隊列長度為0,現在加了一個元素后立即喚醒notEmpty條件if (c == 0)signalNotEmpty(); }private void enqueue(Node<E> node) {// 直接加到last后面last = last.next = node; } private void signalNotEmpty() {final ReentrantLock takeLock = this.takeLock;// 加take鎖takeLock.lock();try {// 喚醒notEmpty條件notEmpty.signal();} finally {// 解鎖takeLock.unlock();} }?take操作:其實和put的操作相反就行,利用takelock加鎖,如果count值為0,那么notEmpty.await()加入到notEmpty的條件隊列中去直至喚醒,喚醒之后取隊頭元素,如果此時count-1還大于0,那么調用signal()喚醒阻塞再notEmpty條件隊列的線程。然后釋放鎖。最后調用signalNotFull,喚醒阻塞在notfull條件隊列上的線程,此過程利用putlock鎖來防止其他線程執行put操作進而可能導致隊列又滿了
public E take() throws InterruptedException {E x;int c = -1;final AtomicInteger count = this.count;final ReentrantLock takeLock = this.takeLock;// 使用takeLock加鎖takeLock.lockInterruptibly();try {// 如果隊列無元素,則阻塞在notEmpty條件上while (count.get() == 0) {notEmpty.await();}// 否則,出隊x = dequeue();// 獲取出隊前隊列的長度c = count.getAndDecrement();// 如果取之前隊列長度大于1,則喚醒notEmptyif (c > 1)notEmpty.signal();} finally {// 釋放鎖takeLock.unlock();}// 如果取之前隊列長度等于容量// 則喚醒notFullif (c == capacity)signalNotFull();return x; }private E dequeue() {// head節點本身是不存儲任何元素的// 這里把head刪除,并把head下一個節點作為新的值// 并把其值置空,返回原來的值Node<E> h = head;Node<E> first = h.next;h.next = h; // help GChead = first;E x = first.item;first.item = null;return x; }private void signalNotFull() {final ReentrantLock putLock = this.putLock;putLock.lock();try {// 喚醒notFullnotFull.signal();} finally {putLock.unlock();} }總結
以上是生活随笔為你收集整理的LinkedBlockingQueue的put,take方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 几个需要扩容的集合
- 下一篇: Spring容器中导入组件方式