ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/LinkedBlockingQueue.java
Revision: 1.32
Committed: Sat Dec 27 17:19:03 2003 UTC (20 years, 5 months ago) by dl
Branch: MAIN
Changes since 1.31: +2 -2 lines
Log Message:
Adapt to AbstractQueuedSynchronizer

File Contents

# User Rev Content
1 dl 1.2 /*
2     * Written by Doug Lea with assistance from members of JCP JSR-166
3     * Expert Group and released to the public domain. Use, modify, and
4     * redistribute this code in any way without acknowledgement.
5     */
6    
7 tim 1.1 package java.util.concurrent;
8 dl 1.2 import java.util.concurrent.atomic.*;
9 dl 1.7 import java.util.concurrent.locks.*;
10 tim 1.1 import java.util.*;
11    
12     /**
13 dholmes 1.14 * An optionally-bounded {@linkplain BlockingQueue blocking queue} based on
14 dholmes 1.8 * linked nodes.
15     * This queue orders elements FIFO (first-in-first-out).
16 tim 1.12 * The <em>head</em> of the queue is that element that has been on the
17 dholmes 1.8 * queue the longest time.
18     * The <em>tail</em> of the queue is that element that has been on the
19 dl 1.20 * queue the shortest time. New elements
20     * are inserted at the tail of the queue, and the queue retrieval
21     * operations obtain elements at the head of the queue.
22 dholmes 1.8 * Linked queues typically have higher throughput than array-based queues but
23     * less predictable performance in most concurrent applications.
24 tim 1.12 *
25 dl 1.3 * <p> The optional capacity bound constructor argument serves as a
26 dholmes 1.8 * way to prevent excessive queue expansion. The capacity, if unspecified,
27     * is equal to {@link Integer#MAX_VALUE}. Linked nodes are
28 dl 1.3 * dynamically created upon each insertion unless this would bring the
29     * queue above capacity.
30 dholmes 1.8 *
31 dl 1.21 * <p>This class implements all of the <em>optional</em> methods
32     * of the {@link Collection} and {@link Iterator} interfaces.
33     *
34 dl 1.6 * @since 1.5
35     * @author Doug Lea
36 dl 1.27 * @param <E> the type of elements held in this collection
37 tim 1.12 *
38 tim 1.1 **/
39 dl 1.2 public class LinkedBlockingQueue<E> extends AbstractQueue<E>
40 tim 1.1 implements BlockingQueue<E>, java.io.Serializable {
41 dl 1.18 private static final long serialVersionUID = -6903933977591709194L;
42 tim 1.1
43 dl 1.2 /*
44     * A variant of the "two lock queue" algorithm. The putLock gates
45     * entry to put (and offer), and has an associated condition for
46     * waiting puts. Similarly for the takeLock. The "count" field
47     * that they both rely on is maintained as an atomic to avoid
48     * needing to get both locks in most cases. Also, to minimize need
49     * for puts to get takeLock and vice-versa, cascading notifies are
50     * used. When a put notices that it has enabled at least one take,
51     * it signals taker. That taker in turn signals others if more
52     * items have been entered since the signal. And symmetrically for
53 tim 1.12 * takes signalling puts. Operations such as remove(Object) and
54 dl 1.2 * iterators acquire both locks.
55     */
56    
57 dl 1.6 /**
58     * Linked list node class
59     */
60 dl 1.2 static class Node<E> {
61 dl 1.6 /** The item, volatile to ensure barrier separating write and read */
62 dl 1.2 volatile E item;
63     Node<E> next;
64     Node(E x) { item = x; }
65     }
66    
67 dl 1.6 /** The capacity bound, or Integer.MAX_VALUE if none */
68 dl 1.2 private final int capacity;
69 dl 1.6
70     /** Current number of elements */
71 dl 1.19 private final AtomicInteger count = new AtomicInteger(0);
72 dl 1.2
73 dl 1.6 /** Head of linked list */
74     private transient Node<E> head;
75    
76 dholmes 1.8 /** Tail of linked list */
77 dl 1.6 private transient Node<E> last;
78 dl 1.2
79 dl 1.6 /** Lock held by take, poll, etc */
80 dl 1.5 private final ReentrantLock takeLock = new ReentrantLock();
81 dl 1.6
82     /** Wait queue for waiting takes */
83 dl 1.32 private final Condition notEmpty = takeLock.newCondition();
84 dl 1.2
85 dl 1.6 /** Lock held by put, offer, etc */
86 dl 1.5 private final ReentrantLock putLock = new ReentrantLock();
87 dl 1.6
88     /** Wait queue for waiting puts */
89 dl 1.32 private final Condition notFull = putLock.newCondition();
90 dl 1.2
91     /**
92     * Signal a waiting take. Called only from put/offer (which do not
93 dl 1.4 * otherwise ordinarily lock takeLock.)
94 dl 1.2 */
95     private void signalNotEmpty() {
96 dl 1.31 final ReentrantLock takeLock = this.takeLock;
97 dl 1.2 takeLock.lock();
98     try {
99     notEmpty.signal();
100 tim 1.17 } finally {
101 dl 1.2 takeLock.unlock();
102     }
103     }
104    
105     /**
106     * Signal a waiting put. Called only from take/poll.
107     */
108     private void signalNotFull() {
109 dl 1.31 final ReentrantLock putLock = this.putLock;
110 dl 1.2 putLock.lock();
111     try {
112     notFull.signal();
113 tim 1.17 } finally {
114 dl 1.2 putLock.unlock();
115     }
116     }
117    
118     /**
119 dholmes 1.8 * Create a node and link it at end of queue
120 dl 1.6 * @param x the item
121 dl 1.2 */
122     private void insert(E x) {
123     last = last.next = new Node<E>(x);
124     }
125    
126     /**
127     * Remove a node from head of queue,
128 dl 1.6 * @return the node
129 dl 1.2 */
130     private E extract() {
131     Node<E> first = head.next;
132     head = first;
133 dl 1.28 E x = first.item;
134 dl 1.2 first.item = null;
135     return x;
136     }
137    
138     /**
139 tim 1.12 * Lock to prevent both puts and takes.
140 dl 1.2 */
141     private void fullyLock() {
142     putLock.lock();
143     takeLock.lock();
144 tim 1.1 }
145 dl 1.2
146     /**
147 tim 1.12 * Unlock to allow both puts and takes.
148 dl 1.2 */
149     private void fullyUnlock() {
150     takeLock.unlock();
151     putLock.unlock();
152     }
153    
154    
155     /**
156 dholmes 1.13 * Creates a <tt>LinkedBlockingQueue</tt> with a capacity of
157 dholmes 1.8 * {@link Integer#MAX_VALUE}.
158 dl 1.2 */
159     public LinkedBlockingQueue() {
160     this(Integer.MAX_VALUE);
161     }
162    
163     /**
164 tim 1.16 * Creates a <tt>LinkedBlockingQueue</tt> with the given (fixed) capacity.
165     *
166 dholmes 1.8 * @param capacity the capacity of this queue.
167     * @throws IllegalArgumentException if <tt>capacity</tt> is not greater
168 tim 1.16 * than zero.
169 dl 1.2 */
170     public LinkedBlockingQueue(int capacity) {
171 dholmes 1.8 if (capacity <= 0) throw new IllegalArgumentException();
172 dl 1.2 this.capacity = capacity;
173 dl 1.6 last = head = new Node<E>(null);
174 dl 1.2 }
175    
176     /**
177 dholmes 1.13 * Creates a <tt>LinkedBlockingQueue</tt> with a capacity of
178 dholmes 1.14 * {@link Integer#MAX_VALUE}, initially containing the elements of the
179 tim 1.12 * given collection,
180 dholmes 1.8 * added in traversal order of the collection's iterator.
181 dholmes 1.9 * @param c the collection of elements to initially contain
182     * @throws NullPointerException if <tt>c</tt> or any element within it
183     * is <tt>null</tt>
184 dl 1.2 */
185 dholmes 1.10 public LinkedBlockingQueue(Collection<? extends E> c) {
186 dl 1.2 this(Integer.MAX_VALUE);
187 tim 1.12 for (Iterator<? extends E> it = c.iterator(); it.hasNext();)
188     add(it.next());
189 dl 1.2 }
190    
191 dholmes 1.9
192 dholmes 1.8 // this doc comment is overridden to remove the reference to collections
193     // greater in size than Integer.MAX_VALUE
194 tim 1.12 /**
195 dl 1.20 * Returns the number of elements in this queue.
196     *
197     * @return the number of elements in this queue.
198 dholmes 1.8 */
199 dl 1.2 public int size() {
200     return count.get();
201 tim 1.1 }
202 dl 1.2
203 dholmes 1.8 // this doc comment is a modified copy of the inherited doc comment,
204     // without the reference to unlimited queues.
205 tim 1.12 /**
206 dholmes 1.13 * Returns the number of elements that this queue can ideally (in
207 dholmes 1.8 * the absence of memory or resource constraints) accept without
208     * blocking. This is always equal to the initial capacity of this queue
209     * less the current <tt>size</tt> of this queue.
210     * <p>Note that you <em>cannot</em> always tell if
211     * an attempt to <tt>add</tt> an element will succeed by
212     * inspecting <tt>remainingCapacity</tt> because it may be the
213     * case that a waiting consumer is ready to <tt>take</tt> an
214     * element out of an otherwise full queue.
215     */
216 dl 1.2 public int remainingCapacity() {
217     return capacity - count.get();
218     }
219    
220 dholmes 1.22 /**
221     * Adds the specified element to the tail of this queue, waiting if
222     * necessary for space to become available.
223 dl 1.23 * @param o the element to add
224     * @throws InterruptedException if interrupted while waiting.
225 dholmes 1.22 * @throws NullPointerException if the specified element is <tt>null</tt>.
226     */
227 dholmes 1.14 public void put(E o) throws InterruptedException {
228     if (o == null) throw new NullPointerException();
229 dl 1.2 // Note: convention in all put/take/etc is to preset
230     // local var holding count negative to indicate failure unless set.
231 tim 1.12 int c = -1;
232 dl 1.31 final ReentrantLock putLock = this.putLock;
233     final AtomicInteger count = this.count;
234 dl 1.2 putLock.lockInterruptibly();
235     try {
236     /*
237     * Note that count is used in wait guard even though it is
238     * not protected by lock. This works because count can
239     * only decrease at this point (all other puts are shut
240     * out by lock), and we (or some other waiting put) are
241     * signalled if it ever changes from
242     * capacity. Similarly for all other uses of count in
243     * other wait guards.
244     */
245     try {
246 tim 1.12 while (count.get() == capacity)
247 dl 1.2 notFull.await();
248 tim 1.17 } catch (InterruptedException ie) {
249 dl 1.2 notFull.signal(); // propagate to a non-interrupted thread
250     throw ie;
251     }
252 dholmes 1.14 insert(o);
253 dl 1.2 c = count.getAndIncrement();
254 dl 1.6 if (c + 1 < capacity)
255 dl 1.2 notFull.signal();
256 tim 1.17 } finally {
257 dl 1.2 putLock.unlock();
258     }
259 tim 1.12 if (c == 0)
260 dl 1.2 signalNotEmpty();
261 tim 1.1 }
262 dl 1.2
263 dholmes 1.22 /**
264     * Inserts the specified element at the tail of this queue, waiting if
265     * necessary up to the specified wait time for space to become available.
266 dl 1.23 * @param o the element to add
267     * @param timeout how long to wait before giving up, in units of
268     * <tt>unit</tt>
269     * @param unit a <tt>TimeUnit</tt> determining how to interpret the
270     * <tt>timeout</tt> parameter
271     * @return <tt>true</tt> if successful, or <tt>false</tt> if
272     * the specified waiting time elapses before space is available.
273     * @throws InterruptedException if interrupted while waiting.
274 dholmes 1.22 * @throws NullPointerException if the specified element is <tt>null</tt>.
275     */
276 dholmes 1.14 public boolean offer(E o, long timeout, TimeUnit unit)
277 dholmes 1.8 throws InterruptedException {
278 tim 1.12
279 dholmes 1.14 if (o == null) throw new NullPointerException();
280 dl 1.2 long nanos = unit.toNanos(timeout);
281     int c = -1;
282 dl 1.31 final ReentrantLock putLock = this.putLock;
283     final AtomicInteger count = this.count;
284 dholmes 1.8 putLock.lockInterruptibly();
285 dl 1.2 try {
286     for (;;) {
287     if (count.get() < capacity) {
288 dholmes 1.14 insert(o);
289 dl 1.2 c = count.getAndIncrement();
290 dl 1.6 if (c + 1 < capacity)
291 dl 1.2 notFull.signal();
292     break;
293     }
294     if (nanos <= 0)
295     return false;
296     try {
297     nanos = notFull.awaitNanos(nanos);
298 tim 1.17 } catch (InterruptedException ie) {
299 dl 1.2 notFull.signal(); // propagate to a non-interrupted thread
300     throw ie;
301     }
302     }
303 tim 1.17 } finally {
304 dl 1.2 putLock.unlock();
305     }
306 tim 1.12 if (c == 0)
307 dl 1.2 signalNotEmpty();
308     return true;
309 tim 1.1 }
310 dl 1.2
311 dl 1.23 /**
312     * Inserts the specified element at the tail of this queue if possible,
313     * returning immediately if this queue is full.
314     *
315     * @param o the element to add.
316     * @return <tt>true</tt> if it was possible to add the element to
317     * this queue, else <tt>false</tt>
318     * @throws NullPointerException if the specified element is <tt>null</tt>
319     */
320 dholmes 1.14 public boolean offer(E o) {
321     if (o == null) throw new NullPointerException();
322 dl 1.31 final AtomicInteger count = this.count;
323 dl 1.2 if (count.get() == capacity)
324     return false;
325 tim 1.12 int c = -1;
326 dl 1.31 final ReentrantLock putLock = this.putLock;
327 dholmes 1.8 putLock.lock();
328 dl 1.2 try {
329     if (count.get() < capacity) {
330 dholmes 1.14 insert(o);
331 dl 1.2 c = count.getAndIncrement();
332 dl 1.6 if (c + 1 < capacity)
333 dl 1.2 notFull.signal();
334     }
335 tim 1.17 } finally {
336 dl 1.2 putLock.unlock();
337     }
338 tim 1.12 if (c == 0)
339 dl 1.2 signalNotEmpty();
340     return c >= 0;
341 tim 1.1 }
342 dl 1.2
343    
344     public E take() throws InterruptedException {
345     E x;
346     int c = -1;
347 dl 1.31 final AtomicInteger count = this.count;
348     final ReentrantLock takeLock = this.takeLock;
349 dl 1.2 takeLock.lockInterruptibly();
350     try {
351     try {
352 tim 1.12 while (count.get() == 0)
353 dl 1.2 notEmpty.await();
354 tim 1.17 } catch (InterruptedException ie) {
355 dl 1.2 notEmpty.signal(); // propagate to a non-interrupted thread
356     throw ie;
357     }
358    
359     x = extract();
360     c = count.getAndDecrement();
361     if (c > 1)
362     notEmpty.signal();
363 tim 1.17 } finally {
364 dl 1.2 takeLock.unlock();
365     }
366 tim 1.12 if (c == capacity)
367 dl 1.2 signalNotFull();
368     return x;
369     }
370    
371     public E poll(long timeout, TimeUnit unit) throws InterruptedException {
372     E x = null;
373     int c = -1;
374 dholmes 1.8 long nanos = unit.toNanos(timeout);
375 dl 1.31 final AtomicInteger count = this.count;
376     final ReentrantLock takeLock = this.takeLock;
377 dl 1.2 takeLock.lockInterruptibly();
378     try {
379     for (;;) {
380     if (count.get() > 0) {
381     x = extract();
382     c = count.getAndDecrement();
383     if (c > 1)
384     notEmpty.signal();
385     break;
386     }
387     if (nanos <= 0)
388     return null;
389     try {
390     nanos = notEmpty.awaitNanos(nanos);
391 tim 1.17 } catch (InterruptedException ie) {
392 dl 1.2 notEmpty.signal(); // propagate to a non-interrupted thread
393     throw ie;
394     }
395     }
396 tim 1.17 } finally {
397 dl 1.2 takeLock.unlock();
398     }
399 tim 1.12 if (c == capacity)
400 dl 1.2 signalNotFull();
401     return x;
402     }
403    
404     public E poll() {
405 dl 1.31 final AtomicInteger count = this.count;
406 dl 1.2 if (count.get() == 0)
407     return null;
408     E x = null;
409 tim 1.12 int c = -1;
410 dl 1.31 final ReentrantLock takeLock = this.takeLock;
411 dl 1.30 takeLock.lock();
412 dl 1.2 try {
413     if (count.get() > 0) {
414     x = extract();
415     c = count.getAndDecrement();
416     if (c > 1)
417     notEmpty.signal();
418     }
419 tim 1.17 } finally {
420 dl 1.2 takeLock.unlock();
421     }
422 tim 1.12 if (c == capacity)
423 dl 1.2 signalNotFull();
424     return x;
425 tim 1.1 }
426 dl 1.2
427    
428     public E peek() {
429     if (count.get() == 0)
430     return null;
431 dl 1.31 final ReentrantLock takeLock = this.takeLock;
432 dholmes 1.8 takeLock.lock();
433 dl 1.2 try {
434     Node<E> first = head.next;
435     if (first == null)
436     return null;
437     else
438     return first.item;
439 tim 1.17 } finally {
440 dl 1.2 takeLock.unlock();
441     }
442 tim 1.1 }
443    
444 dholmes 1.9 public boolean remove(Object o) {
445     if (o == null) return false;
446 dl 1.2 boolean removed = false;
447     fullyLock();
448     try {
449     Node<E> trail = head;
450     Node<E> p = head.next;
451     while (p != null) {
452 dholmes 1.9 if (o.equals(p.item)) {
453 dl 1.2 removed = true;
454     break;
455     }
456     trail = p;
457     p = p.next;
458     }
459     if (removed) {
460     p.item = null;
461     trail.next = p.next;
462     if (count.getAndDecrement() == capacity)
463     notFull.signalAll();
464     }
465 tim 1.17 } finally {
466 dl 1.2 fullyUnlock();
467     }
468     return removed;
469 tim 1.1 }
470 dl 1.2
471     public Object[] toArray() {
472     fullyLock();
473     try {
474     int size = count.get();
475 tim 1.12 Object[] a = new Object[size];
476 dl 1.2 int k = 0;
477 tim 1.12 for (Node<E> p = head.next; p != null; p = p.next)
478 dl 1.2 a[k++] = p.item;
479     return a;
480 tim 1.17 } finally {
481 dl 1.2 fullyUnlock();
482     }
483 tim 1.1 }
484 dl 1.2
485     public <T> T[] toArray(T[] a) {
486     fullyLock();
487     try {
488     int size = count.get();
489     if (a.length < size)
490 dl 1.4 a = (T[])java.lang.reflect.Array.newInstance
491     (a.getClass().getComponentType(), size);
492 tim 1.12
493 dl 1.2 int k = 0;
494 tim 1.12 for (Node p = head.next; p != null; p = p.next)
495 dl 1.2 a[k++] = (T)p.item;
496     return a;
497 tim 1.17 } finally {
498 dl 1.2 fullyUnlock();
499     }
500 tim 1.1 }
501 dl 1.2
502     public String toString() {
503     fullyLock();
504     try {
505     return super.toString();
506 tim 1.17 } finally {
507 dl 1.2 fullyUnlock();
508     }
509 tim 1.1 }
510 dl 1.2
511 dl 1.24 public void clear() {
512     fullyLock();
513     try {
514     head.next = null;
515     if (count.getAndSet(0) == capacity)
516     notFull.signalAll();
517     } finally {
518     fullyUnlock();
519     }
520     }
521    
522     public int drainTo(Collection<? super E> c) {
523     if (c == null)
524     throw new NullPointerException();
525     if (c == this)
526     throw new IllegalArgumentException();
527     Node first;
528     fullyLock();
529     try {
530     first = head.next;
531     head.next = null;
532     if (count.getAndSet(0) == capacity)
533     notFull.signalAll();
534     } finally {
535     fullyUnlock();
536     }
537     // Transfer the elements outside of locks
538     int n = 0;
539 dl 1.29 for (Node<E> p = first; p != null; p = p.next) {
540     c.add(p.item);
541 dl 1.24 p.item = null;
542     ++n;
543     }
544     return n;
545     }
546    
547     public int drainTo(Collection<? super E> c, int maxElements) {
548     if (c == null)
549     throw new NullPointerException();
550     if (c == this)
551     throw new IllegalArgumentException();
552     if (maxElements <= 0)
553     return 0;
554     fullyLock();
555     try {
556     int n = 0;
557 dl 1.29 Node<E> p = head.next;
558 dl 1.24 while (p != null && n < maxElements) {
559 dl 1.29 c.add(p.item);
560 dl 1.24 p.item = null;
561     p = p.next;
562     ++n;
563     }
564     if (n != 0) {
565     head.next = p;
566     if (count.getAndAdd(-n) == capacity)
567     notFull.signalAll();
568     }
569     return n;
570     } finally {
571     fullyUnlock();
572     }
573     }
574    
575 dholmes 1.14 /**
576     * Returns an iterator over the elements in this queue in proper sequence.
577 dl 1.15 * The returned <tt>Iterator</tt> is a "weakly consistent" iterator that
578     * will never throw {@link java.util.ConcurrentModificationException},
579     * and guarantees to traverse elements as they existed upon
580     * construction of the iterator, and may (but is not guaranteed to)
581     * reflect any modifications subsequent to construction.
582 dholmes 1.14 *
583     * @return an iterator over the elements in this queue in proper sequence.
584     */
585 dl 1.2 public Iterator<E> iterator() {
586     return new Itr();
587 tim 1.1 }
588 dl 1.2
589     private class Itr implements Iterator<E> {
590 tim 1.12 /*
591 dl 1.4 * Basic weak-consistent iterator. At all times hold the next
592     * item to hand out so that if hasNext() reports true, we will
593     * still have it to return even if lost race with a take etc.
594     */
595 dl 1.31 private Node<E> current;
596     private Node<E> lastRet;
597     private E currentElement;
598 tim 1.12
599 dl 1.2 Itr() {
600 dl 1.31 final ReentrantLock putLock = LinkedBlockingQueue.this.putLock;
601     final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock;
602     putLock.lock();
603     takeLock.lock();
604 dl 1.2 try {
605     current = head.next;
606 dl 1.4 if (current != null)
607     currentElement = current.item;
608 tim 1.17 } finally {
609 dl 1.31 takeLock.unlock();
610     putLock.unlock();
611 dl 1.2 }
612     }
613 tim 1.12
614     public boolean hasNext() {
615 dl 1.2 return current != null;
616     }
617    
618 tim 1.12 public E next() {
619 dl 1.31 final ReentrantLock putLock = LinkedBlockingQueue.this.putLock;
620     final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock;
621     putLock.lock();
622     takeLock.lock();
623 dl 1.2 try {
624     if (current == null)
625     throw new NoSuchElementException();
626 dl 1.4 E x = currentElement;
627 dl 1.2 lastRet = current;
628     current = current.next;
629 dl 1.4 if (current != null)
630     currentElement = current.item;
631 dl 1.2 return x;
632 tim 1.17 } finally {
633 dl 1.31 takeLock.unlock();
634     putLock.unlock();
635 dl 1.2 }
636     }
637    
638 tim 1.12 public void remove() {
639 dl 1.2 if (lastRet == null)
640 tim 1.12 throw new IllegalStateException();
641 dl 1.31 final ReentrantLock putLock = LinkedBlockingQueue.this.putLock;
642     final ReentrantLock takeLock = LinkedBlockingQueue.this.takeLock;
643     putLock.lock();
644     takeLock.lock();
645 dl 1.2 try {
646     Node<E> node = lastRet;
647     lastRet = null;
648     Node<E> trail = head;
649     Node<E> p = head.next;
650     while (p != null && p != node) {
651     trail = p;
652     p = p.next;
653     }
654     if (p == node) {
655     p.item = null;
656     trail.next = p.next;
657     int c = count.getAndDecrement();
658     if (c == capacity)
659     notFull.signalAll();
660     }
661 tim 1.17 } finally {
662 dl 1.31 takeLock.unlock();
663     putLock.unlock();
664 dl 1.2 }
665     }
666 tim 1.1 }
667 dl 1.2
668     /**
669     * Save the state to a stream (that is, serialize it).
670     *
671     * @serialData The capacity is emitted (int), followed by all of
672     * its elements (each an <tt>Object</tt>) in the proper order,
673     * followed by a null
674 dl 1.6 * @param s the stream
675 dl 1.2 */
676     private void writeObject(java.io.ObjectOutputStream s)
677     throws java.io.IOException {
678    
679 tim 1.12 fullyLock();
680 dl 1.2 try {
681     // Write out any hidden stuff, plus capacity
682     s.defaultWriteObject();
683    
684     // Write out all elements in the proper order.
685 tim 1.12 for (Node<E> p = head.next; p != null; p = p.next)
686 dl 1.2 s.writeObject(p.item);
687    
688     // Use trailing null as sentinel
689     s.writeObject(null);
690 tim 1.17 } finally {
691 dl 1.2 fullyUnlock();
692     }
693 tim 1.1 }
694    
695 dl 1.2 /**
696 dholmes 1.8 * Reconstitute this queue instance from a stream (that is,
697 dl 1.2 * deserialize it).
698 dl 1.6 * @param s the stream
699 dl 1.2 */
700     private void readObject(java.io.ObjectInputStream s)
701     throws java.io.IOException, ClassNotFoundException {
702 tim 1.12 // Read in capacity, and any hidden stuff
703     s.defaultReadObject();
704 dl 1.2
705 dl 1.19 count.set(0);
706     last = head = new Node<E>(null);
707    
708 dl 1.6 // Read in all elements and place in queue
709 dl 1.2 for (;;) {
710     E item = (E)s.readObject();
711     if (item == null)
712     break;
713     add(item);
714     }
715 tim 1.1 }
716     }