Java多線程系列--「JUC集合」08之 LinkedBlockingQueue

 

概要

本章介紹JUC包中的LinkedBlockingQueue。內容包括:
LinkedBlockingQueue介紹
LinkedBlockingQueue原理和數據結構
LinkedBlockingQueue函數列表
LinkedBlockingQueue源碼分析(JDK1.7.0_40版本)
LinkedBlockingQueue示例html

轉載請註明出處:http://www.cnblogs.com/skywang12345/p/3503458.htmljava

 

LinkedBlockingQueue介紹

LinkedBlockingQueue是一個單向鏈表實現的阻塞隊列。該隊列按 FIFO(先進先出)排序元素,新元素插入到隊列的尾部,而且隊列獲取操做會得到位於隊列頭部的元素。連接隊列的吞吐量一般要高於基於數組的隊列,可是在大多數併發應用程序中,其可預知的性能要低。node

此外,LinkedBlockingQueue仍是可選容量的(防止過分膨脹),便可以指定隊列的容量。若是不指定,默認容量大小等於Integer.MAX_VALUE。數組

 

LinkedBlockingQueue原理和數據結構

LinkedBlockingQueue的數據結構,以下圖所示:安全

說明
1. LinkedBlockingQueue繼承於AbstractQueue,它本質上是一個FIFO(先進先出)的隊列。
2. LinkedBlockingQueue實現了BlockingQueue接口,它支持多線程併發。當多線程競爭同一個資源時,某線程獲取到該資源以後,其它線程須要阻塞等待。
3. LinkedBlockingQueue是經過單鏈表實現的。
(01) head是鏈表的表頭。取出數據時,都是從表頭head處插入。
(02) last是鏈表的表尾。新增數據時,都是從表尾last處插入。
(03) count是鏈表的實際大小,即當前鏈表中包含的節點個數。
(04) capacity是列表的容量,它是在建立鏈表時指定的。
(05) putLock是插入鎖,takeLock是取出鎖;notEmpty是「非空條件」,notFull是「未滿條件」。經過它們對鏈表進行併發控制。
       LinkedBlockingQueue在實現「多線程對競爭資源的互斥訪問」時,對於「插入」和「取出(刪除)」操做分別使用了不一樣的鎖。對於插入操做,經過「插入鎖putLock」進行同步;對於取出操做,經過「取出鎖takeLock」進行同步。
       此外,插入鎖putLock和「非滿條件notFull」相關聯,取出鎖takeLock和「非空條件notEmpty」相關聯。經過notFull和notEmpty更細膩的控制鎖。數據結構

     -- 若某線程(線程A)要取出數據時,隊列正好爲空,則該線程會執行notEmpty.await()進行等待;當其它某個線程(線程B)向隊列中插入了數據以後,會調用notEmpty.signal()喚醒「notEmpty上的等待線程」。此時,線程A會被喚醒從而得以繼續運行。 此外,線程A在執行取操做前,會獲取takeLock,在取操做執行完畢再釋放takeLock。 -- 若某線程(線程H)要插入數據時,隊列已滿,則該線程會它執行notFull.await()進行等待;當其它某個線程(線程I)取出數據以後,會調用notFull.signal()喚醒「notFull上的等待線程」。此時,線程H就會被喚醒從而得以繼續運行。 此外,線程H在執行插入操做前,會獲取putLock,在插入操做執行完畢才釋放putLock。

關於ReentrantLock 和 Condition等更多的內容,能夠參考:
    (01) Java多線程系列--「JUC鎖」02之 互斥鎖ReentrantLock
    (02) Java多線程系列--「JUC鎖」03之 公平鎖(一)
    (03) Java多線程系列--「JUC鎖」04之 公平鎖(二)
    (04) Java多線程系列--「JUC鎖」05之 非公平鎖
    (05) Java多線程系列--「JUC鎖」06之 Condition條件多線程

 

LinkedBlockingQueue函數列表

// 建立一個容量爲 Integer.MAX_VALUE 的 LinkedBlockingQueue。
LinkedBlockingQueue()
// 建立一個容量是 Integer.MAX_VALUE 的 LinkedBlockingQueue,最初包含給定 collection 的元素,元素按該 collection 迭代器的遍歷順序添加。
LinkedBlockingQueue(Collection<? extends E> c)
// 建立一個具備給定(固定)容量的 LinkedBlockingQueue。
LinkedBlockingQueue(int capacity)

// 從隊列完全移除全部元素。
void clear()
// 移除此隊列中全部可用的元素,並將它們添加到給定 collection 中。
int drainTo(Collection<? super E> c)
// 最多今後隊列中移除給定數量的可用元素,並將這些元素添加到給定 collection 中。
int drainTo(Collection<? super E> c, int maxElements)
// 返回在隊列中的元素上按適當順序進行迭代的迭代器。
Iterator<E> iterator()
// 將指定元素插入到此隊列的尾部(若是當即可行且不會超出此隊列的容量),在成功時返回 true,若是此隊列已滿,則返回 false。
boolean offer(E e)
// 將指定元素插入到此隊列的尾部,若有必要,則等待指定的時間以使空間變得可用。
boolean offer(E e, long timeout, TimeUnit unit)
// 獲取但不移除此隊列的頭;若是此隊列爲空,則返回 null。
E peek()
// 獲取並移除此隊列的頭,若是此隊列爲空,則返回 null。
E poll()
// 獲取並移除此隊列的頭部,在指定的等待時間前等待可用的元素(若是有必要)。
E poll(long timeout, TimeUnit unit)
// 將指定元素插入到此隊列的尾部,若有必要,則等待空間變得可用。
void put(E e)
// 返回理想狀況下(沒有內存和資源約束)此隊列可接受而且不會被阻塞的附加元素數量。
int remainingCapacity()
// 今後隊列移除指定元素的單個實例(若是存在)。
boolean remove(Object o)
// 返回隊列中的元素個數。
int size()
// 獲取並移除此隊列的頭部,在元素變得可用以前一直等待(若是有必要)。
E take()
// 返回按適當順序包含此隊列中全部元素的數組。
Object[] toArray()
// 返回按適當順序包含此隊列中全部元素的數組;返回數組的運行時類型是指定數組的運行時類型。
<T> T[] toArray(T[] a)
// 返回此 collection 的字符串表示形式。
String toString()

 

LinkedBlockingQueue源碼分析(JDK1.7.0_40版本)

LinkedBlockingQueue.java的完整源碼以下:併發

  1 /*
  2  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  3  *
  4  *
  5  *
  6  *
  7  *
  8  *
  9  *
 10  *
 11  *
 12  *
 13  *
 14  *
 15  *
 16  *
 17  *
 18  *
 19  *
 20  *
 21  *
 22  *
 23  */
 24 
 25 /*
 26  *
 27  *
 28  *
 29  *
 30  *
 31  * Written by Doug Lea with assistance from members of JCP JSR-166
 32  * Expert Group and released to the public domain, as explained at
 33  * http://creativecommons.org/publicdomain/zero/1.0/
 34  */
 35 
 36 package java.util.concurrent;
 37 
 38 import java.util.concurrent.atomic.AtomicInteger;
 39 import java.util.concurrent.locks.Condition;
 40 import java.util.concurrent.locks.ReentrantLock;
 41 import java.util.AbstractQueue;
 42 import java.util.Collection;
 43 import java.util.Iterator;
 44 import java.util.NoSuchElementException;
 45 
 46 /**
 47  * An optionally-bounded {@linkplain BlockingQueue blocking queue} based on
 48  * linked nodes.
 49  * This queue orders elements FIFO (first-in-first-out).
 50  * The <em>head</em> of the queue is that element that has been on the
 51  * queue the longest time.
 52  * The <em>tail</em> of the queue is that element that has been on the
 53  * queue the shortest time. New elements
 54  * are inserted at the tail of the queue, and the queue retrieval
 55  * operations obtain elements at the head of the queue.
 56  * Linked queues typically have higher throughput than array-based queues but
 57  * less predictable performance in most concurrent applications.
 58  *
 59  * <p> The optional capacity bound constructor argument serves as a
 60  * way to prevent excessive queue expansion. The capacity, if unspecified,
 61  * is equal to {@link Integer#MAX_VALUE}.  Linked nodes are
 62  * dynamically created upon each insertion unless this would bring the
 63  * queue above capacity.
 64  *
 65  * <p>This class and its iterator implement all of the
 66  * <em>optional</em> methods of the {@link Collection} and {@link
 67  * Iterator} interfaces.
 68  *
 69  * <p>This class is a member of the
 70  * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 71  * Java Collections Framework</a>.
 72  *
 73  * @since 1.5
 74  * @author Doug Lea
 75  * @param <E> the type of elements held in this collection
 76  *
 77  */
 78 public class LinkedBlockingQueue<E> extends AbstractQueue<E>
 79         implements BlockingQueue<E>, java.io.Serializable {
 80     private static final long serialVersionUID = -6903933977591709194L;
 81 
 82     /*
 83      * A variant of the "two lock queue" algorithm.  The putLock gates
 84      * entry to put (and offer), and has an associated condition for
 85      * waiting puts.  Similarly for the takeLock.  The "count" field
 86      * that they both rely on is maintained as an atomic to avoid
 87      * needing to get both locks in most cases. Also, to minimize need
 88      * for puts to get takeLock and vice-versa, cascading notifies are
 89      * used. When a put notices that it has enabled at least one take,
 90      * it signals taker. That taker in turn signals others if more
 91      * items have been entered since the signal. And symmetrically for
 92      * takes signalling puts. Operations such as remove(Object) and
 93      * iterators acquire both locks.
 94      *
 95      * Visibility between writers and readers is provided as follows:
 96      *
 97      * Whenever an element is enqueued, the putLock is acquired and
 98      * count updated.  A subsequent reader guarantees visibility to the
 99      * enqueued Node by either acquiring the putLock (via fullyLock)
100      * or by acquiring the takeLock, and then reading n = count.get();
101      * this gives visibility to the first n items.
102      *
103      * To implement weakly consistent iterators, it appears we need to
104      * keep all Nodes GC-reachable from a predecessor dequeued Node.
105      * That would cause two problems:
106      * - allow a rogue Iterator to cause unbounded memory retention
107      * - cause cross-generational linking of old Nodes to new Nodes if
108      *   a Node was tenured while live, which generational GCs have a
109      *   hard time dealing with, causing repeated major collections.
110      * However, only non-deleted Nodes need to be reachable from
111      * dequeued Nodes, and reachability does not necessarily have to
112      * be of the kind understood by the GC.  We use the trick of
113      * linking a Node that has just been dequeued to itself.  Such a
114      * self-link implicitly means to advance to head.next.
115      */
116 
117     /**
118      * Linked list node class
119      */
120     static class Node<E> {
121         E item;
122 
123         /**
124          * One of:
125          * - the real successor Node
126          * - this Node, meaning the successor is head.next
127          * - null, meaning there is no successor (this is the last node)
128          */
129         Node<E> next;
130 
131         Node(E x) { item = x; }
132     }
133 
134     /** The capacity bound, or Integer.MAX_VALUE if none */
135     private final int capacity;
136 
137     /** Current number of elements */
138     private final AtomicInteger count = new AtomicInteger(0);
139 
140     /**
141      * Head of linked list.
142      * Invariant: head.item == null
143      */
144     private transient Node<E> head;
145 
146     /**
147      * Tail of linked list.
148      * Invariant: last.next == null
149      */
150     private transient Node<E> last;
151 
152     /** Lock held by take, poll, etc */
153     private final ReentrantLock takeLock = new ReentrantLock();
154 
155     /** Wait queue for waiting takes */
156     private final Condition notEmpty = takeLock.newCondition();
157 
158     /** Lock held by put, offer, etc */
159     private final ReentrantLock putLock = new ReentrantLock();
160 
161     /** Wait queue for waiting puts */
162     private final Condition notFull = putLock.newCondition();
163 
164     /**
165      * Signals a waiting take. Called only from put/offer (which do not
166      * otherwise ordinarily lock takeLock.)
167      */
168     private void signalNotEmpty() {
169         final ReentrantLock takeLock = this.takeLock;
170         takeLock.lock();
171         try {
172             notEmpty.signal();
173         } finally {
174             takeLock.unlock();
175         }
176     }
177 
178     /**
179      * Signals a waiting put. Called only from take/poll.
180      */
181     private void signalNotFull() {
182         final ReentrantLock putLock = this.putLock;
183         putLock.lock();
184         try {
185             notFull.signal();
186         } finally {
187             putLock.unlock();
188         }
189     }
190 
191     /**
192      * Links node at end of queue.
193      *
194      * @param node the node
195      */
196     private void enqueue(Node<E> node) {
197         // assert putLock.isHeldByCurrentThread();
198         // assert last.next == null;
199         last = last.next = node;
200     }
201 
202     /**
203      * Removes a node from head of queue.
204      *
205      * @return the node
206      */
207     private E dequeue() {
208         // assert takeLock.isHeldByCurrentThread();
209         // assert head.item == null;
210         Node<E> h = head;
211         Node<E> first = h.next;
212         h.next = h; // help GC
213         head = first;
214         E x = first.item;
215         first.item = null;
216         return x;
217     }
218 
219     /**
220      * Lock to prevent both puts and takes.
221      */
222     void fullyLock() {
223         putLock.lock();
224         takeLock.lock();
225     }
226 
227     /**
228      * Unlock to allow both puts and takes.
229      */
230     void fullyUnlock() {
231         takeLock.unlock();
232         putLock.unlock();
233     }
234 
235 //     /**
236 //      * Tells whether both locks are held by current thread.
237 //      */
238 //     boolean isFullyLocked() {
239 //         return (putLock.isHeldByCurrentThread() &&
240 //                 takeLock.isHeldByCurrentThread());
241 //     }
242 
243     /**
244      * Creates a {@code LinkedBlockingQueue} with a capacity of
245      * {@link Integer#MAX_VALUE}.
246      */
247     public LinkedBlockingQueue() {
248         this(Integer.MAX_VALUE);
249     }
250 
251     /**
252      * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
253      *
254      * @param capacity the capacity of this queue
255      * @throws IllegalArgumentException if {@code capacity} is not greater
256      *         than zero
257      */
258     public LinkedBlockingQueue(int capacity) {
259         if (capacity <= 0) throw new IllegalArgumentException();
260         this.capacity = capacity;
261         last = head = new Node<E>(null);
262     }
263 
264     /**
265      * Creates a {@code LinkedBlockingQueue} with a capacity of
266      * {@link Integer#MAX_VALUE}, initially containing the elements of the
267      * given collection,
268      * added in traversal order of the collection's iterator.
269      *
270      * @param c the collection of elements to initially contain
271      * @throws NullPointerException if the specified collection or any
272      *         of its elements are null
273      */
274     public LinkedBlockingQueue(Collection<? extends E> c) {
275         this(Integer.MAX_VALUE);
276         final ReentrantLock putLock = this.putLock;
277         putLock.lock(); // Never contended, but necessary for visibility
278         try {
279             int n = 0;
280             for (E e : c) {
281                 if (e == null)
282                     throw new NullPointerException();
283                 if (n == capacity)
284                     throw new IllegalStateException("Queue full");
285                 enqueue(new Node<E>(e));
286                 ++n;
287             }
288             count.set(n);
289         } finally {
290             putLock.unlock();
291         }
292     }
293 
294 
295     // this doc comment is overridden to remove the reference to collections
296     // greater in size than Integer.MAX_VALUE
297     /**
298      * Returns the number of elements in this queue.
299      *
300      * @return the number of elements in this queue
301      */
302     public int size() {
303         return count.get();
304     }
305 
306     // this doc comment is a modified copy of the inherited doc comment,
307     // without the reference to unlimited queues.
308     /**
309      * Returns the number of additional elements that this queue can ideally
310      * (in the absence of memory or resource constraints) accept without
311      * blocking. This is always equal to the initial capacity of this queue
312      * less the current {@code size} of this queue.
313      *
314      * <p>Note that you <em>cannot</em> always tell if an attempt to insert
315      * an element will succeed by inspecting {@code remainingCapacity}
316      * because it may be the case that another thread is about to
317      * insert or remove an element.
318      */
319     public int remainingCapacity() {
320         return capacity - count.get();
321     }
322 
323     /**
324      * Inserts the specified element at the tail of this queue, waiting if
325      * necessary for space to become available.
326      *
327      * @throws InterruptedException {@inheritDoc}
328      * @throws NullPointerException {@inheritDoc}
329      */
330     public void put(E e) throws InterruptedException {
331         if (e == null) throw new NullPointerException();
332         // Note: convention in all put/take/etc is to preset local var
333         // holding count negative to indicate failure unless set.
334         int c = -1;
335         Node<E> node = new Node(e);
336         final ReentrantLock putLock = this.putLock;
337         final AtomicInteger count = this.count;
338         putLock.lockInterruptibly();
339         try {
340             /*
341              * Note that count is used in wait guard even though it is
342              * not protected by lock. This works because count can
343              * only decrease at this point (all other puts are shut
344              * out by lock), and we (or some other waiting put) are
345              * signalled if it ever changes from capacity. Similarly
346              * for all other uses of count in other wait guards.
347              */
348             while (count.get() == capacity) {
349                 notFull.await();
350             }
351             enqueue(node);
352             c = count.getAndIncrement();
353             if (c + 1 < capacity)
354                 notFull.signal();
355         } finally {
356             putLock.unlock();
357         }
358         if (c == 0)
359             signalNotEmpty();
360     }
361 
362     /**
363      * Inserts the specified element at the tail of this queue, waiting if
364      * necessary up to the specified wait time for space to become available.
365      *
366      * @return {@code true} if successful, or {@code false} if
367      *         the specified waiting time elapses before space is available.
368      * @throws InterruptedException {@inheritDoc}
369      * @throws NullPointerException {@inheritDoc}
370      */
371     public boolean offer(E e, long timeout, TimeUnit unit)
372         throws InterruptedException {
373 
374         if (e == null) throw new NullPointerException();
375         long nanos = unit.toNanos(timeout);
376         int c = -1;
377         final ReentrantLock putLock = this.putLock;
378         final AtomicInteger count = this.count;
379         putLock.lockInterruptibly();
380         try {
381             while (count.get() == capacity) {
382                 if (nanos <= 0)
383                     return false;
384                 nanos = notFull.awaitNanos(nanos);
385             }
386             enqueue(new Node<E>(e));
387             c = count.getAndIncrement();
388             if (c + 1 < capacity)
389                 notFull.signal();
390         } finally {
391             putLock.unlock();
392         }
393         if (c == 0)
394             signalNotEmpty();
395         return true;
396     }
397 
398     /**
399      * Inserts the specified element at the tail of this queue if it is
400      * possible to do so immediately without exceeding the queue's capacity,
401      * returning {@code true} upon success and {@code false} if this queue
402      * is full.
403      * When using a capacity-restricted queue, this method is generally
404      * preferable to method {@link BlockingQueue#add add}, which can fail to
405      * insert an element only by throwing an exception.
406      *
407      * @throws NullPointerException if the specified element is null
408      */
409     public boolean offer(E e) {
410         if (e == null) throw new NullPointerException();
411         final AtomicInteger count = this.count;
412         if (count.get() == capacity)
413             return false;
414         int c = -1;
415         Node<E> node = new Node(e);
416         final ReentrantLock putLock = this.putLock;
417         putLock.lock();
418         try {
419             if (count.get() < capacity) {
420                 enqueue(node);
421                 c = count.getAndIncrement();
422                 if (c + 1 < capacity)
423                     notFull.signal();
424             }
425         } finally {
426             putLock.unlock();
427         }
428         if (c == 0)
429             signalNotEmpty();
430         return c >= 0;
431     }
432 
433 
434     public E take() throws InterruptedException {
435         E x;
436         int c = -1;
437         final AtomicInteger count = this.count;
438         final ReentrantLock takeLock = this.takeLock;
439         takeLock.lockInterruptibly();
440         try {
441             while (count.get() == 0) {
442                 notEmpty.await();
443             }
444             x = dequeue();
445             c = count.getAndDecrement();
446             if (c > 1)
447                 notEmpty.signal();
448         } finally {
449             takeLock.unlock();
450         }
451         if (c == capacity)
452             signalNotFull();
453         return x;
454     }
455 
456     public E poll(long timeout, TimeUnit unit) throws InterruptedException {
457         E x = null;
458         int c = -1;
459         long nanos = unit.toNanos(timeout);
460         final AtomicInteger count = this.count;
461         final ReentrantLock takeLock = this.takeLock;
462         takeLock.lockInterruptibly();
463         try {
464             while (count.get() == 0) {
465                 if (nanos <= 0)
466                     return null;
467                 nanos = notEmpty.awaitNanos(nanos);
468             }
469             x = dequeue();
470             c = count.getAndDecrement();
471             if (c > 1)
472                 notEmpty.signal();
473         } finally {
474             takeLock.unlock();
475         }
476         if (c == capacity)
477             signalNotFull();
478         return x;
479     }
480 
481     public E poll() {
482         final AtomicInteger count = this.count;
483         if (count.get() == 0)
484             return null;
485         E x = null;
486         int c = -1;
487         final ReentrantLock takeLock = this.takeLock;
488         takeLock.lock();
489         try {
490             if (count.get() > 0) {
491                 x = dequeue();
492                 c = count.getAndDecrement();
493                 if (c > 1)
494                     notEmpty.signal();
495             }
496         } finally {
497             takeLock.unlock();
498         }
499         if (c == capacity)
500             signalNotFull();
501         return x;
502     }
503 
504     public E peek() {
505         if (count.get() == 0)
506             return null;
507         final ReentrantLock takeLock = this.takeLock;
508         takeLock.lock();
509         try {
510             Node<E> first = head.next;
511             if (first == null)
512                 return null;
513             else
514                 return first.item;
515         } finally {
516             takeLock.unlock();
517         }
518     }
519 
520     /**
521      * Unlinks interior Node p with predecessor trail.
522      */
523     void unlink(Node<E> p, Node<E> trail) {
524         // assert isFullyLocked();
525         // p.next is not changed, to allow iterators that are
526         // traversing p to maintain their weak-consistency guarantee.
527         p.item = null;
528         trail.next = p.next;
529         if (last == p)
530             last = trail;
531         if (count.getAndDecrement() == capacity)
532             notFull.signal();
533     }
534 
535     /**
536      * Removes a single instance of the specified element from this queue,
537      * if it is present.  More formally, removes an element {@code e} such
538      * that {@code o.equals(e)}, if this queue contains one or more such
539      * elements.
540      * Returns {@code true} if this queue contained the specified element
541      * (or equivalently, if this queue changed as a result of the call).
542      *
543      * @param o element to be removed from this queue, if present
544      * @return {@code true} if this queue changed as a result of the call
545      */
546     public boolean remove(Object o) {
547         if (o == null) return false;
548         fullyLock();
549         try {
550             for (Node<E> trail = head, p = trail.next;
551                  p != null;
552                  trail = p, p = p.next) {
553                 if (o.equals(p.item)) {
554                     unlink(p, trail);
555                     return true;
556                 }
557             }
558             return false;
559         } finally {
560             fullyUnlock();
561         }
562     }
563 
564     /**
565      * Returns {@code true} if this queue contains the specified element.
566      * More formally, returns {@code true} if and only if this queue contains
567      * at least one element {@code e} such that {@code o.equals(e)}.
568      *
569      * @param o object to be checked for containment in this queue
570      * @return {@code true} if this queue contains the specified element
571      */
572     public boolean contains(Object o) {
573         if (o == null) return false;
574         fullyLock();
575         try {
576             for (Node<E> p = head.next; p != null; p = p.next)
577                 if (o.equals(p.item))
578                     return true;
579             return false;
580         } finally {
581             fullyUnlock();
582         }
583     }
584 
585     /**
586      * Returns an array containing all of the elements in this queue, in
587      * proper sequence.
588      *
589      * <p>The returned array will be "safe" in that no references to it are
590      * maintained by this queue.  (In other words, this method must allocate
591      * a new array).  The caller is thus free to modify the returned array.
592      *
593      * <p>This method acts as bridge between array-based and collection-based
594      * APIs.
595      *
596      * @return an array containing all of the elements in this queue
597      */
598     public Object[] toArray() {
599         fullyLock();
600         try {
601             int size = count.get();
602             Object[] a = new Object[size];
603             int k = 0;
604             for (Node<E> p = head.next; p != null; p = p.next)
605                 a[k++] = p.item;
606             return a;
607         } finally {
608             fullyUnlock();
609         }
610     }
611 
612     /**
613      * Returns an array containing all of the elements in this queue, in
614      * proper sequence; the runtime type of the returned array is that of
615      * the specified array.  If the queue fits in the specified array, it
616      * is returned therein.  Otherwise, a new array is allocated with the
617      * runtime type of the specified array and the size of this queue.
618      *
619      * <p>If this queue fits in the specified array with room to spare
620      * (i.e., the array has more elements than this queue), the element in
621      * the array immediately following the end of the queue is set to
622      * {@code null}.
623      *
624      * <p>Like the {@link #toArray()} method, this method acts as bridge between
625      * array-based and collection-based APIs.  Further, this method allows
626      * precise control over the runtime type of the output array, and may,
627      * under certain circumstances, be used to save allocation costs.
628      *
629      * <p>Suppose {@code x} is a queue known to contain only strings.
630      * The following code can be used to dump the queue into a newly
631      * allocated array of {@code String}:
632      *
633      * <pre>
634      *     String[] y = x.toArray(new String[0]);</pre>
635      *
636      * Note that {@code toArray(new Object[0])} is identical in function to
637      * {@code toArray()}.
638      *
639      * @param a the array into which the elements of the queue are to
640      *          be stored, if it is big enough; otherwise, a new array of the
641      *          same runtime type is allocated for this purpose
642      * @return an array containing all of the elements in this queue
643      * @throws ArrayStoreException if the runtime type of the specified array
644      *         is not a supertype of the runtime type of every element in
645      *         this queue
646      * @throws NullPointerException if the specified array is null
647      */
648     @SuppressWarnings("unchecked")
649     public <T> T[] toArray(T[] a) {
650         fullyLock();
651         try {
652             int size = count.get();
653             if (a.length < size)
654                 a = (T[])java.lang.reflect.Array.newInstance
655                     (a.getClass().getComponentType(), size);
656 
657             int k = 0;
658             for (Node<E> p = head.next; p != null; p = p.next)
659                 a[k++] = (T)p.item;
660             if (a.length > k)
661                 a[k] = null;
662             return a;
663         } finally {
664             fullyUnlock();
665         }
666     }
667 
668     public String toString() {
669         fullyLock();
670         try {
671             Node<E> p = head.next;
672             if (p == null)
673                 return "[]";
674 
675             StringBuilder sb = new StringBuilder();
676             sb.append('[');
677             for (;;) {
678                 E e = p.item;
679                 sb.append(e == this ? "(this Collection)" : e);
680                 p = p.next;
681                 if (p == null)
682                     return sb.append(']').toString();
683                 sb.append(',').append(' ');
684             }
685         } finally {
686             fullyUnlock();
687         }
688     }
689 
690     /**
691      * Atomically removes all of the elements from this queue.
692      * The queue will be empty after this call returns.
693      */
694     public void clear() {
695         fullyLock();
696         try {
697             for (Node<E> p, h = head; (p = h.next) != null; h = p) {
698                 h.next = h;
699                 p.item = null;
700             }
701             head = last;
702             // assert head.item == null && head.next == null;
703             if (count.getAndSet(0) == capacity)
704                 notFull.signal();
705         } finally {
706             fullyUnlock();
707         }
708     }
709 
710     /**
711      * @throws UnsupportedOperationException {@inheritDoc}
712      * @throws ClassCastException            {@inheritDoc}
713      * @throws NullPointerException          {@inheritDoc}
714      * @throws IllegalArgumentException      {@inheritDoc}
715      */
716     public int drainTo(Collection<? super E> c) {
717         return drainTo(c, Integer.MAX_VALUE);
718     }
719 
720     /**
721      * @throws UnsupportedOperationException {@inheritDoc}
722      * @throws ClassCastException            {@inheritDoc}
723      * @throws NullPointerException          {@inheritDoc}
724      * @throws IllegalArgumentException      {@inheritDoc}
725      */
726     public int drainTo(Collection<? super E> c, int maxElements) {
727         if (c == null)
728             throw new NullPointerException();
729         if (c == this)
730             throw new IllegalArgumentException();
731         boolean signalNotFull = false;
732         final ReentrantLock takeLock = this.takeLock;
733         takeLock.lock();
734         try {
735             int n = Math.min(maxElements, count.get());
736             // count.get provides visibility to first n Nodes
737             Node<E> h = head;
738             int i = 0;
739             try {
740                 while (i < n) {
741                     Node<E> p = h.next;
742                     c.add(p.item);
743                     p.item = null;
744                     h.next = h;
745                     h = p;
746                     ++i;
747                 }
748                 return n;
749             } finally {
750                 // Restore invariants even if c.add() threw
751                 if (i > 0) {
752                     // assert h.item == null;
753                     head = h;
754                     signalNotFull = (count.getAndAdd(-i) == capacity);
755                 }
756             }
757         } finally {
758             takeLock.unlock();
759             if (signalNotFull)
760                 signalNotFull();
761         }
762     }
763 
764     /**
765      * Returns an iterator over the elements in this queue in proper sequence.
766      * The elements will be returned in order from first (head) to last (tail).
767      *
768      * <p>The returned iterator is a "weakly consistent" iterator that
769      * will never throw {@link java.util.ConcurrentModificationException
770      * ConcurrentModificationException}, and guarantees to traverse
771      * elements as they existed upon construction of the iterator, and
772      * may (but is not guaranteed to) reflect any modifications
773      * subsequent to construction.
774      *
775      * @return an iterator over the elements in this queue in proper sequence
776      */
777     public Iterator<E> iterator() {
778       return new Itr();
779     }
780 
781     private class Itr implements Iterator<E> {
782         /*
783          * Basic weakly-consistent iterator.  At all times hold the next
784          * item to hand out so that if hasNext() reports true, we will
785          * still have it to return even if lost race with a take etc.
786          */
787         private Node<E> current;
788         private Node<E> lastRet;
789         private E currentElement;
790 
791         Itr() {
792             fullyLock();
793             try {
794                 current = head.next;
795                 if (current != null)
796                     currentElement = current.item;
797             } finally {
798                 fullyUnlock();
799             }
800         }
801 
802         public boolean hasNext() {
803             return current != null;
804         }
805 
806         /**
807          * Returns the next live successor of p, or null if no such.
808          *
809          * Unlike other traversal methods, iterators need to handle both:
810          * - dequeued nodes (p.next == p)
811          * - (possibly multiple) interior removed nodes (p.item == null)
812          */
813         private Node<E> nextNode(Node<E> p) {
814             for (;;) {
815                 Node<E> s = p.next;
816                 if (s == p)
817                     return head.next;
818                 if (s == null || s.item != null)
819                     return s;
820                 p = s;
821             }
822         }
823 
824         public E next() {
825             fullyLock();
826             try {
827                 if (current == null)
828                     throw new NoSuchElementException();
829                 E x = currentElement;
830                 lastRet = current;
831                 current = nextNode(current);
832                 currentElement = (current == null) ? null : current.item;
833                 return x;
834             } finally {
835                 fullyUnlock();
836             }
837         }
838 
839         public void remove() {
840             if (lastRet == null)
841                 throw new IllegalStateException();
842             fullyLock();
843             try {
844                 Node<E> node = lastRet;
845                 lastRet = null;
846                 for (Node<E> trail = head, p = trail.next;
847                      p != null;
848                      trail = p, p = p.next) {
849                     if (p == node) {
850                         unlink(p, trail);
851                         break;
852                     }
853                 }
854             } finally {
855                 fullyUnlock();
856             }
857         }
858     }
859 
860     /**
861      * Save the state to a stream (that is, serialize it).
862      *
863      * @serialData The capacity is emitted (int), followed by all of
864      * its elements (each an {@code Object}) in the proper order,
865      * followed by a null
866      * @param s the stream
867      */
868     private void writeObject(java.io.ObjectOutputStream s)
869         throws java.io.IOException {
870 
871         fullyLock();
872         try {
873             // Write out any hidden stuff, plus capacity
874             s.defaultWriteObject();
875 
876             // Write out all elements in the proper order.
877             for (Node<E> p = head.next; p != null; p = p.next)
878                 s.writeObject(p.item);
879 
880             // Use trailing null as sentinel
881             s.writeObject(null);
882         } finally {
883             fullyUnlock();
884         }
885     }
886 
887     /**
888      * Reconstitute this queue instance from a stream (that is,
889      * deserialize it).
890      *
891      * @param s the stream
892      */
893     private void readObject(java.io.ObjectInputStream s)
894         throws java.io.IOException, ClassNotFoundException {
895         // Read in capacity, and any hidden stuff
896         s.defaultReadObject();
897 
898         count.set(0);
899         last = head = new Node<E>(null);
900 
901         // Read in all elements and place in queue
902         for (;;) {
903             @SuppressWarnings("unchecked")
904             E item = (E)s.readObject();
905             if (item == null)
906                 break;
907             add(item);
908         }
909     }
910 }
View Code


下面從LinkedBlockingQueue的建立,添加,刪除,遍歷這幾個方面對它進行分析。app

1. 建立框架

下面以LinkedBlockingQueue(int capacity)來進行說明。

public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    last = head = new Node<E>(null);
}

說明
(01) capacity是「鏈式阻塞隊列」的容量。
(02) head和last是「鏈式阻塞隊列」的首節點和尾節點。它們在LinkedBlockingQueue中的聲明以下:

// 容量
private final int capacity;
// 當前數量
private final AtomicInteger count = new AtomicInteger(0);
private transient Node<E> head; // 鏈表的表頭
private transient Node<E> last; // 鏈表的表尾
// 用於控制「刪除元素」的互斥鎖takeLock 和 鎖對應的「非空條件」notEmpty
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
// 用於控制「添加元素」的互斥鎖putLock 和 鎖對應的「非滿條件」notFull
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();

 

鏈表的節點定義以下:

static class Node<E> {
    E item;         // 數據
    Node<E> next;   // 下一個節點的指針

    Node(E x) { item = x; }
}


2. 添加

下面以offer(E e)爲例,對LinkedBlockingQueue的添加方法進行說明。

public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    // 若是「隊列已滿」,則返回false,表示插入失敗。
    final AtomicInteger count = this.count;
    if (count.get() == capacity)
        return false;
    int c = -1;
    // 新建「節點e」
    Node<E> node = new Node(e);
    final ReentrantLock putLock = this.putLock;
    // 獲取「插入鎖putLock」
    putLock.lock();
    try {
        // 再次對「隊列是否是滿」的進行判斷。
        // 若「隊列未滿」,則插入節點。
        if (count.get() < capacity) {
            // 插入節點
            enqueue(node);
            // 將「當前節點數量」+1,並返回「原始的數量」
            c = count.getAndIncrement();
            // 若是在插入元素以後,隊列仍然未滿,則喚醒notFull上的等待線程。
            if (c + 1 < capacity)
                notFull.signal();
        }
    } finally {
        // 釋放「插入鎖putLock」
        putLock.unlock();
    }
    // 若是在插入節點前,隊列爲空;則插入節點後,喚醒notEmpty上的等待線程
    if (c == 0)
        signalNotEmpty();
    return c >= 0;
}

說明:offer()的做用很簡單,就是將元素E添加到隊列的末尾。

enqueue()的源碼以下:

private void enqueue(Node<E> node) {
    // assert putLock.isHeldByCurrentThread();
    // assert last.next == null;
    last = last.next = node;
}

enqueue()的做用是將node添加到隊列末尾,並設置node爲新的尾節點!

signalNotEmpty()的源碼以下:

private void signalNotEmpty() {
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
}

signalNotEmpty()的做用是喚醒notEmpty上的等待線程。

 

3. 取出

下面以take()爲例,對LinkedBlockingQueue的取出方法進行說明。

public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    // 獲取「取出鎖」,若當前線程是中斷狀態,則拋出InterruptedException異常
    takeLock.lockInterruptibly();
    try {
        // 若「隊列爲空」,則一直等待。
        while (count.get() == 0) {
            notEmpty.await();
        }
        // 取出元素
        x = dequeue();
        // 取出元素以後,將「節點數量」-1;並返回「原始的節點數量」。
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        // 釋放「取出鎖」
        takeLock.unlock();
    }
    // 若是在「取出元素以前」,隊列是滿的;則在取出元素以後,喚醒notFull上的等待線程。
    if (c == capacity)
        signalNotFull();
    return x;
}

說明:take()的做用是取出並返回隊列的頭。若隊列爲空,則一直等待。

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;
}

dequeue()的做用就是刪除隊列的頭節點,並將表頭指向「原頭節點的下一個節點」。

signalNotFull()的源碼以下:

private void signalNotFull() {
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        notFull.signal();
    } finally {
        putLock.unlock();
    }
}

signalNotFull()的做用就是喚醒notFull上的等待線程。

 

4. 遍歷

下面對LinkedBlockingQueue的遍歷方法進行說明。

public Iterator<E> iterator() {
  return new Itr();
}

iterator()其實是返回一個Iter對象。

Itr類的定義以下:

private class Itr implements Iterator<E> {
    // 當前節點
    private Node<E> current;
    // 上一次返回的節點
    private Node<E> lastRet;
    // 當前節點對應的值
    private E currentElement;

    Itr() {
        // 同時獲取「插入鎖putLock」 和 「取出鎖takeLock」
        fullyLock();
        try {
            // 設置「當前元素」爲「隊列表頭的下一節點」,即爲隊列的第一個有效節點
            current = head.next;
            if (current != null)
                currentElement = current.item;
        } finally {
            // 釋放「插入鎖putLock」 和 「取出鎖takeLock」
            fullyUnlock();
        }
    }

    // 返回「下一個節點是否爲null」
    public boolean hasNext() {
        return current != null;
    }

    private Node<E> nextNode(Node<E> p) {
        for (;;) {
            Node<E> s = p.next;
            if (s == p)
                return head.next;
            if (s == null || s.item != null)
                return s;
            p = s;
        }
    }

    // 返回下一個節點
    public E next() {
        fullyLock();
        try {
            if (current == null)
                throw new NoSuchElementException();
            E x = currentElement;
            lastRet = current;
            current = nextNode(current);
            currentElement = (current == null) ? null : current.item;
            return x;
        } finally {
            fullyUnlock();
        }
    }

    // 刪除下一個節點
    public void remove() {
        if (lastRet == null)
            throw new IllegalStateException();
        fullyLock();
        try {
            Node<E> node = lastRet;
            lastRet = null;
            for (Node<E> trail = head, p = trail.next;
                 p != null;
                 trail = p, p = p.next) {
                if (p == node) {
                    unlink(p, trail);
                    break;
                }
            }
        } finally {
            fullyUnlock();
        }
    }
}

 

LinkedBlockingQueue示例

 1 import java.util.*;
 2 import java.util.concurrent.*;
 3 
 4 /*
 5  *   LinkedBlockingQueue是「線程安全」的隊列,而LinkedList是非線程安全的。
 6  *
 7  *   下面是「多個線程同時操做而且遍歷queue」的示例
 8  *   (01) 當queue是LinkedBlockingQueue對象時,程序能正常運行。
 9  *   (02) 當queue是LinkedList對象時,程序會產生ConcurrentModificationException異常。
10  *
11  * @author skywang
12  */
13 public class LinkedBlockingQueueDemo1 {
14 
15     // TODO: queue是LinkedList對象時,程序會出錯。
16     //private static Queue<String> queue = new LinkedList<String>();
17     private static Queue<String> queue = new LinkedBlockingQueue<String>();
18     public static void main(String[] args) {
19     
20         // 同時啓動兩個線程對queue進行操做!
21         new MyThread("ta").start();
22         new MyThread("tb").start();
23     }
24 
25     private static void printAll() {
26         String value;
27         Iterator iter = queue.iterator();
28         while(iter.hasNext()) {
29             value = (String)iter.next();
30             System.out.print(value+", ");
31         }
32         System.out.println();
33     }
34 
35     private static class MyThread extends Thread {
36         MyThread(String name) {
37             super(name);
38         }
39         @Override
40         public void run() {
41                 int i = 0;
42             while (i++ < 6) {
43                 // 「線程名」 + "-" + "序號"
44                 String val = Thread.currentThread().getName()+i;
45                 queue.add(val);
46                 // 經過「Iterator」遍歷queue。
47                 printAll();
48             }
49         }
50     }
51 }

(某一次)運行結果

tb1, ta1, 
tb1, ta1, ta2, 
tb1, ta1, ta2, ta3, 
tb1, ta1, ta2, ta3, ta4, 
tb1, ta1, tb1, ta2, ta1, ta3, ta2, ta4, ta3, ta5, 
ta4, tb1, ta5, ta1, ta6, 
ta2, tb1, ta3, ta1, ta4, ta2, ta5, ta3, ta6, ta4, tb2, 
ta5, ta6, tb2, 
tb1, ta1, ta2, ta3, ta4, ta5, ta6, tb2, tb3, 
tb1, ta1, ta2, ta3, ta4, ta5, ta6, tb2, tb3, tb4, 
tb1, ta1, ta2, ta3, ta4, ta5, ta6, tb2, tb3, tb4, tb5, 
tb1, ta1, ta2, ta3, ta4, ta5, ta6, tb2, tb3, tb4, tb5, tb6,

結果說明
示例程序中,啓動兩個線程(線程ta和線程tb)分別對LinkedBlockingQueue進行操做。以線程ta而言,它會先獲取「線程名」+「序號」,而後將該字符串添加到LinkedBlockingQueue中;接着,遍歷並輸出LinkedBlockingQueue中的所有元素。 線程tb的操做和線程ta同樣,只不過線程tb的名字和線程ta的名字不一樣。
當queue是LinkedBlockingQueue對象時,程序能正常運行。若是將queue改成LinkedList時,程序會產生ConcurrentModificationException異常。

 

 


更多內容

1. Java多線程系列--「JUC集合」01之 框架

2. Java多線程系列目錄(共xx篇)

相關文章
相關標籤/搜索