概要
本章介绍JUC包中的LinkedBlockingDeque。内容包括:
LinkedBlockingDeque介绍
LinkedBlockingDeque原理和数据结构
LinkedBlockingDeque函数列表
LinkedBlockingDeque源码分析(JDK1.7.0_40版本)
LinkedBlockingDeque示例
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3503480.html
LinkedBlockingDeque介绍
LinkedBlockingDeque是双向链表实现的双向并发阻塞队列。该阻塞队列同时支持FIFO和FILO两种操作方式,即可以从队列的头和尾同时操作(插入/删除);并且,该阻塞队列是支持线程安全。
此外,LinkedBlockingDeque还是可选容量的(防止过度膨胀),即可以指定队列的容量。如果不指定,默认容量大小等于Integer.MAX_VALUE。
LinkedBlockingDeque原理和数据结构
LinkedBlockingDeque的数据结构,如下图所示:
说明:
1. LinkedBlockingDeque继承于AbstractQueue,它本质上是一个支持FIFO和FILO的双向的队列。
2. LinkedBlockingDeque实现了BlockingDeque接口,它支持多线程并发。当多线程竞争同一个资源时,某线程获取到该资源之后,其它线程需要阻塞等待。
3. LinkedBlockingDeque是通过双向链表实现的。
3.1 first是双向链表的表头。
3.2 last是双向链表的表尾。
3.3 count是LinkedBlockingDeque的实际大小,即双向链表中当前节点个数。
3.4 capacity是LinkedBlockingDeque的容量,它是在创建LinkedBlockingDeque时指定的。
3.5 lock是控制对LinkedBlockingDeque的互斥锁,当多个线程竞争同时访问LinkedBlockingDeque时,某线程获取到了互斥锁lock,其它线程则需要阻塞等待,直到该线程释放lock,其它线程才有机会获取lock从而获取cpu执行权。
3.6 notEmpty和notFull分别是“非空条件”和“未满条件”。通过它们能够更加细腻进行并发控制。
-- 若某线程(线程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条件
LinkedBlockingDeque函数列表
// 创建一个容量为 Integer.MAX_VALUE 的 LinkedBlockingDeque。
LinkedBlockingDeque()
// 创建一个容量为 Integer.MAX_VALUE 的 LinkedBlockingDeque,最初包含给定 collection 的元素,以该 collection 迭代器的遍历顺序添加。
LinkedBlockingDeque(Collection<? extends E> c)
// 创建一个具有给定(固定)容量的 LinkedBlockingDeque。
LinkedBlockingDeque(int capacity)
// 在不违反容量限制的情况下,将指定的元素插入此双端队列的末尾。
boolean add(E e)
// 如果立即可行且不违反容量限制,则将指定的元素插入此双端队列的开头;如果当前没有空间可用,则抛出 IllegalStateException。
void addFirst(E e)
// 如果立即可行且不违反容量限制,则将指定的元素插入此双端队列的末尾;如果当前没有空间可用,则抛出 IllegalStateException。
void addLast(E e)
// 以原子方式 (atomically) 从此双端队列移除所有元素。
void clear()
// 如果此双端队列包含指定的元素,则返回 true。
boolean contains(Object o)
// 返回在此双端队列的元素上以逆向连续顺序进行迭代的迭代器。
Iterator<E> descendingIterator()
// 移除此队列中所有可用的元素,并将它们添加到给定 collection 中。
int drainTo(Collection<? super E> c)
// 最多从此队列中移除给定数量的可用元素,并将这些元素添加到给定 collection 中。
int drainTo(Collection<? super E> c, int maxElements)
// 获取但不移除此双端队列表示的队列的头部。
E element()
// 获取,但不移除此双端队列的第一个元素。
E getFirst()
// 获取,但不移除此双端队列的最后一个元素。
E getLast()
// 返回在此双端队列元素上以恰当顺序进行迭代的迭代器。
Iterator<E> iterator()
// 如果立即可行且不违反容量限制,则将指定的元素插入此双端队列表示的队列中(即此双端队列的尾部),并在成功时返回 true;如果当前没有空间可用,则返回 false。
boolean offer(E e)
// 将指定的元素插入此双端队列表示的队列中(即此双端队列的尾部),必要时将在指定的等待时间内一直等待可用空间。
boolean offer(E e, long timeout, TimeUnit unit)
// 如果立即可行且不违反容量限制,则将指定的元素插入此双端队列的开头,并在成功时返回 true;如果当前没有空间可用,则返回 false。
boolean offerFirst(E e)
// 将指定的元素插入此双端队列的开头,必要时将在指定的等待时间内等待可用空间。
boolean offerFirst(E e, long timeout, TimeUnit unit)
// 如果立即可行且不违反容量限制,则将指定的元素插入此双端队列的末尾,并在成功时返回 true;如果当前没有空间可用,则返回 false。
boolean offerLast(E e)
// 将指定的元素插入此双端队列的末尾,必要时将在指定的等待时间内等待可用空间。
boolean offerLast(E e, long timeout, TimeUnit unit)
// 获取但不移除此双端队列表示的队列的头部(即此双端队列的第一个元素);如果此双端队列为空,则返回 null。
E peek()
// 获取,但不移除此双端队列的第一个元素;如果此双端队列为空,则返回 null。
E peekFirst()
// 获取,但不移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null。
E peekLast()
// 获取并移除此双端队列表示的队列的头部(即此双端队列的第一个元素);如果此双端队列为空,则返回 null。
E poll()
// 获取并移除此双端队列表示的队列的头部(即此双端队列的第一个元素),如有必要将在指定的等待时间内等待可用元素。
E poll(long timeout, TimeUnit unit)
// 获取并移除此双端队列的第一个元素;如果此双端队列为空,则返回 null。
E pollFirst()
// 获取并移除此双端队列的第一个元素,必要时将在指定的等待时间等待可用元素。
E pollFirst(long timeout, TimeUnit unit)
// 获取并移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null。
E pollLast()
// 获取并移除此双端队列的最后一个元素,必要时将在指定的等待时间内等待可用元素。
E pollLast(long timeout, TimeUnit unit)
// 从此双端队列所表示的堆栈中弹出一个元素。
E pop()
// 将元素推入此双端队列表示的栈。
void push(E e)
// 将指定的元素插入此双端队列表示的队列中(即此双端队列的尾部),必要时将一直等待可用空间。
void put(E e)
// 将指定的元素插入此双端队列的开头,必要时将一直等待可用空间。
void putFirst(E e)
// 将指定的元素插入此双端队列的末尾,必要时将一直等待可用空间。
void putLast(E e)
// 返回理想情况下(没有内存和资源约束)此双端队列可不受阻塞地接受的额外元素数。
int remainingCapacity()
// 获取并移除此双端队列表示的队列的头部。
E remove()
// 从此双端队列移除第一次出现的指定元素。
boolean remove(Object o)
// 获取并移除此双端队列第一个元素。
E removeFirst()
// 从此双端队列移除第一次出现的指定元素。
boolean removeFirstOccurrence(Object o)
// 获取并移除此双端队列的最后一个元素。
E removeLast()
// 从此双端队列移除最后一次出现的指定元素。
boolean removeLastOccurrence(Object o)
// 返回此双端队列中的元素数。
int size()
// 获取并移除此双端队列表示的队列的头部(即此双端队列的第一个元素),必要时将一直等待可用元素。
E take()
// 获取并移除此双端队列的第一个元素,必要时将一直等待可用元素。
E takeFirst()
// 获取并移除此双端队列的最后一个元素,必要时将一直等待可用元素。
E takeLast()
// 返回以恰当顺序(从第一个元素到最后一个元素)包含此双端队列所有元素的数组。
Object[] toArray()
// 返回以恰当顺序包含此双端队列所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
<T> T[] toArray(T[] a)
// 返回此 collection 的字符串表示形式。
String toString()
LinkedBlockingDeque源码分析(JDK1.7.0_40版本)
LinkedBlockingDeque.java的完整源码如下:
1 /*View Code
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.AbstractQueue;
39 import java.util.Collection;
40 import java.util.Iterator;
41 import java.util.NoSuchElementException;
42 import java.util.concurrent.locks.Condition;
43 import java.util.concurrent.locks.ReentrantLock;
44
45 /**
46 * An optionally-bounded {@linkplain BlockingDeque blocking deque} based on
47 * linked nodes.
48 *
49 * <p> The optional capacity bound constructor argument serves as a
50 * way to prevent excessive expansion. The capacity, if unspecified,
51 * is equal to {@link Integer#MAX_VALUE}. Linked nodes are
52 * dynamically created upon each insertion unless this would bring the
53 * deque above capacity.
54 *
55 * <p>Most operations run in constant time (ignoring time spent
56 * blocking). Exceptions include {@link #remove(Object) remove},
57 * {@link #removeFirstOccurrence removeFirstOccurrence}, {@link
58 * #removeLastOccurrence removeLastOccurrence}, {@link #contains
59 * contains}, {@link #iterator iterator.remove()}, and the bulk
60 * operations, all of which run in linear time.
61 *
62 * <p>This class and its iterator implement all of the
63 * <em>optional</em> methods of the {@link Collection} and {@link
64 * Iterator} interfaces.
65 *
66 * <p>This class is a member of the
67 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
68 * Java Collections Framework</a>.
69 *
70 * @since 1.6
71 * @author Doug Lea
72 * @param <E> the type of elements held in this collection
73 */
74 public class LinkedBlockingDeque<E>
75 extends AbstractQueue<E>
76 implements BlockingDeque<E>, java.io.Serializable {
77
78 /*
79 * Implemented as a simple doubly-linked list protected by a
80 * single lock and using conditions to manage blocking.
81 *
82 * To implement weakly consistent iterators, it appears we need to
83 * keep all Nodes GC-reachable from a predecessor dequeued Node.
84 * That would cause two problems:
85 * - allow a rogue Iterator to cause unbounded memory retention
86 * - cause cross-generational linking of old Nodes to new Nodes if
87 * a Node was tenured while live, which generational GCs have a
88 * hard time dealing with, causing repeated major collections.
89 * However, only non-deleted Nodes need to be reachable from
90 * dequeued Nodes, and reachability does not necessarily have to
91 * be of the kind understood by the GC. We use the trick of
92 * linking a Node that has just been dequeued to itself. Such a
93 * self-link implicitly means to jump to "first" (for next links)
94 * or "last" (for prev links).
95 */
96
97 /*
98 * We have "diamond" multiple interface/abstract class inheritance
99 * here, and that introduces ambiguities. Often we want the
100 * BlockingDeque javadoc combined with the AbstractQueue
101 * implementation, so a lot of method specs are duplicated here.
102 */
103
104 private static final long serialVersionUID = -387911632671998426L;
105
106 /** Doubly-linked list node class */
107 static final class Node<E> {
108 /**
109 * The item, or null if this node has been removed.
110 */
111 E item;
112
113 /**
114 * One of:
115 * - the real predecessor Node
116 * - this Node, meaning the predecessor is tail
117 * - null, meaning there is no predecessor
118 */
119 Node<E> prev;
120
121 /**
122 * One of:
123 * - the real successor Node
124 * - this Node, meaning the successor is head
125 * - null, meaning there is no successor
126 */
127 Node<E> next;
128
129 Node(E x) {
130 item = x;
131 }
132 }
133
134 /**
135 * Pointer to first node.
136 * Invariant: (first == null && last == null) ||
137 * (first.prev == null && first.item != null)
138 */
139 transient Node<E> first;
140
141 /**
142 * Pointer to last node.
143 * Invariant: (first == null && last == null) ||
144 * (last.next == null && last.item != null)
145 */
146 transient Node<E> last;
147
148 /** Number of items in the deque */
149 private transient int count;
150
151 /** Maximum number of items in the deque */
152 private final int capacity;
153
154 /** Main lock guarding all access */
155 final ReentrantLock lock = new ReentrantLock();
156
157 /** Condition for waiting takes */
158 private final Condition notEmpty = lock.newCondition();
159
160 /** Condition for waiting puts */
161 private final Condition notFull = lock.newCondition();
162
163 /**
164 * Creates a {@code LinkedBlockingDeque} with a capacity of
165 * {@link Integer#MAX_VALUE}.
166 */
167 public LinkedBlockingDeque() {
168 this(Integer.MAX_VALUE);
169 }
170
171 /**
172 * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity.
173 *
174 * @param capacity the capacity of this deque
175 * @throws IllegalArgumentException if {@code capacity} is less than 1
176 */
177 public LinkedBlockingDeque(int capacity) {
178 if (capacity <= 0) throw new IllegalArgumentException();
179 this.capacity = capacity;
180 }
181
182 /**
183 * Creates a {@code LinkedBlockingDeque} with a capacity of
184 * {@link Integer#MAX_VALUE}, initially containing the elements of
185 * the given collection, added in traversal order of the
186 * collection's iterator.
187 *
188 * @param c the collection of elements to initially contain
189 * @throws NullPointerException if the specified collection or any
190 * of its elements are null
191 */
192 public LinkedBlockingDeque(Collection<? extends E> c) {
193 this(Integer.MAX_VALUE);
194 final ReentrantLock lock = this.lock;
195 lock.lock(); // Never contended, but necessary for visibility
196 try {
197 for (E e : c) {
198 if (e == null)
199 throw new NullPointerException();
200 if (!linkLast(new Node<E>(e)))
201 throw new IllegalStateException("Deque full");
202 }
203 } finally {
204 lock.unlock();
205 }
206 }
207
208
209 // Basic linking and unlinking operations, called only while holding lock
210
211 /**
212 * Links node as first element, or returns false if full.
213 */
214 private boolean linkFirst(Node<E> node) {
215 // assert lock.isHeldByCurrentThread();
216 if (count >= capacity)
217 return false;
218 Node<E> f = first;
219 node.next = f;
220 first = node;
221 if (last == null)
222 last = node;
223 else
224 f.prev = node;
225 ++count;
226 notEmpty.signal();
227 return true;
228 }
229
230 /**
231 * Links node as last element, or returns false if full.
232 */
233 private boolean linkLast(Node<E> node) {
234 // assert lock.isHeldByCurrentThread();
235 if (count >= capacity)
236 return false;
237 Node<E> l = last;
238 node.prev = l;
239 last = node;
240 if (first == null)
241 first = node;
242 else
243 l.next = node;
244 ++count;
245 notEmpty.signal();
246 return true;
247 }
248
249 /**
250 * Removes and returns first element, or null if empty.
251 */
252 private E unlinkFirst() {
253 // assert lock.isHeldByCurrentThread();
254 Node<E> f = first;
255 if (f == null)
256 return null;
257 Node<E> n = f.next;
258 E item = f.item;
259 f.item = null;
260 f.next = f; // help GC
261 first = n;
262 if (n == null)
263 last = null;
264 else
265 n.prev = null;
266 --count;
267 notFull.signal();
268 return item;
269 }
270
271 /**
272 * Removes and returns last element, or null if empty.
273 */
274 private E unlinkLast() {
275 // assert lock.isHeldByCurrentThread();
276 Node<E> l = last;
277 if (l == null)
278 return null;
279 Node<E> p = l.prev;
280 E item = l.item;
281 l.item = null;
282 l.prev = l; // help GC
283 last = p;
284 if (p == null)
285 first = null;
286 else
287 p.next = null;
288 --count;
289 notFull.signal();
290 return item;
291 }
292
293 /**
294 * Unlinks x.
295 */
296 void unlink(Node<E> x) {
297 // assert lock.isHeldByCurrentThread();
298 Node<E> p = x.prev;
299 Node<E> n = x.next;
300 if (p == null) {
301 unlinkFirst();
302 } else if (n == null) {
303 unlinkLast();
304 } else {
305 p.next = n;
306 n.prev = p;
307 x.item = null;
308 // Don't mess with x's links. They may still be in use by
309 // an iterator.
310 --count;
311 notFull.signal();
312 }
313 }
314
315 // BlockingDeque methods
316
317 /**
318 * @throws IllegalStateException {@inheritDoc}
319 * @throws NullPointerException {@inheritDoc}
320 */
321 public void addFirst(E e) {
322 if (!offerFirst(e))
323 throw new IllegalStateException("Deque full");
324 }
325
326 /**
327 * @throws IllegalStateException {@inheritDoc}
328 * @throws NullPointerException {@inheritDoc}
329 */
330 public void addLast(E e) {
331 if (!offerLast(e))
332 throw new IllegalStateException("Deque full");
333 }
334
335 /**
336 * @throws NullPointerException {@inheritDoc}
337 */
338 public boolean offerFirst(E e) {
339 if (e == null) throw new NullPointerException();
340 Node<E> node = new Node<E>(e);
341 final ReentrantLock lock = this.lock;
342 lock.lock();
343 try {
344 return linkFirst(node);
345 } finally {
346 lock.unlock();
347 }
348 }
349
350 /**
351 * @throws NullPointerException {@inheritDoc}
352 */
353 public boolean offerLast(E e) {
354 if (e == null) throw new NullPointerException();
355 Node<E> node = new Node<E>(e);
356 final ReentrantLock lock = this.lock;
357 lock.lock();
358 try {
359 return linkLast(node);
360 } finally {
361 lock.unlock();
362 }
363 }
364
365 /**
366 * @throws NullPointerException {@inheritDoc}
367 * @throws InterruptedException {@inheritDoc}
368 */
369 public void putFirst(E e) throws InterruptedException {
370 if (e == null) throw new NullPointerException();
371 Node<E> node = new Node<E>(e);
372 final ReentrantLock lock = this.lock;
373 lock.lock();
374 try {
375 while (!linkFirst(node))
376 notFull.await();
377 } finally {
378 lock.unlock();
379 }
380 }
381
382 /**
383 * @throws NullPointerException {@inheritDoc}
384 * @throws InterruptedException {@inheritDoc}
385 */
386 public void putLast(E e) throws InterruptedException {
387 if (e == null) throw new NullPointerException();
388 Node<E> node = new Node<E>(e);
389 final ReentrantLock lock = this.lock;
390 lock.lock();
391 try {
392 while (!linkLast(node))
393 notFull.await();
394 } finally {
395 lock.unlock();
396 }
397 }
398
399 /**
400 * @throws NullPointerException {@inheritDoc}
401 * @throws InterruptedException {@inheritDoc}
402 */
403 public boolean offerFirst(E e, long timeout, TimeUnit unit)
404 throws InterruptedException {
405 if (e == null) throw new NullPointerException();
406 Node<E> node = new Node<E>(e);
407 long nanos = unit.toNanos(timeout);
408 final ReentrantLock lock = this.lock;
409 lock.lockInterruptibly();
410 try {
411 while (!linkFirst(node)) {
412 if (nanos <= 0)
413 return false;
414 nanos = notFull.awaitNanos(nanos);
415 }
416 return true;
417 } finally {
418 lock.unlock();
419 }
420 }
421
422 /**
423 * @throws NullPointerException {@inheritDoc}
424 * @throws InterruptedException {@inheritDoc}
425 */
426 public boolean offerLast(E e, long timeout, TimeUnit unit)
427 throws InterruptedException {
428 if (e == null) throw new NullPointerException();
429 Node<E> node = new Node<E>(e);
430 long nanos = unit.toNanos(timeout);
431 final ReentrantLock lock = this.lock;
432 lock.lockInterruptibly();
433 try {
434 while (!linkLast(node)) {
435 if (nanos <= 0)
436 return false;
437 nanos = notFull.awaitNanos(nanos);
438 }
439 return true;
440 } finally {
441 lock.unlock();
442 }
443 }
444
445 /**
446 * @throws NoSuchElementException {@inheritDoc}
447 */
448 public E removeFirst() {
449 E x = pollFirst();
450 if (x == null) throw new NoSuchElementException();
451 return x;
452 }
453
454 /**
455 * @throws NoSuchElementException {@inheritDoc}
456 */
457 public E removeLast() {
458 E x = pollLast();
459 if (x == null) throw new NoSuchElementException();
460 return x;
461 }
462
463 public E pollFirst() {
464 final ReentrantLock lock = this.lock;
465 lock.lock();
466 try {
467 return unlinkFirst();
468 } finally {
469 lock.unlock();
470 }
471 }
472
473 public E pollLast() {
474 final ReentrantLock lock = this.lock;
475 lock.lock();
476 try {
477 return unlinkLast();
478 } finally {
479 lock.unlock();
480 }
481 }
482
483 public E takeFirst() throws InterruptedException {
484 final ReentrantLock lock = this.lock;
485 lock.lock();
486 try {
487 E x;
488 while ( (x = unlinkFirst()) == null)
489 notEmpty.await();
490 return x;
491 } finally {
492 lock.unlock();
493 }
494 }
495
496 public E takeLast() throws InterruptedException {
497 final ReentrantLock lock = this.lock;
498 lock.lock();
499 try {
500 E x;
501 while ( (x = unlinkLast()) == null)
502 notEmpty.await();
503 return x;
504 } finally {
505 lock.unlock();
506 }
507 }
508
509 public E pollFirst(long timeout, TimeUnit unit)
510 throws InterruptedException {
511 long nanos = unit.toNanos(timeout);
512 final ReentrantLock lock = this.lock;
513 lock.lockInterruptibly();
514 try {
515 E x;
516 while ( (x = unlinkFirst()) == null) {
517 if (nanos <= 0)
518 return null;
519 nanos = notEmpty.awaitNanos(nanos);
520 }
521 return x;
522 } finally {
523 lock.unlock();
524 }
525 }
526
527 public E pollLast(long timeout, TimeUnit unit)
528 throws InterruptedException {
529 long nanos = unit.toNanos(timeout);
530 final ReentrantLock lock = this.lock;
531 lock.lockInterruptibly();
532 try {
533 E x;
534 while ( (x = unlinkLast()) == null) {
535 if (nanos <= 0)
536 return null;
537 nanos = notEmpty.awaitNanos(nanos);
538 }
539 return x;
540 } finally {
541 lock.unlock();
542 }
543 }
544
545 /**
546 * @throws NoSuchElementException {@inheritDoc}
547 */
548 public E getFirst() {
549 E x = peekFirst();
550 if (x == null) throw new NoSuchElementException();
551 return x;
552 }
553
554 /**
555 * @throws NoSuchElementException {@inheritDoc}
556 */
557 public E getLast() {
558 E x = peekLast();
559 if (x == null) throw new NoSuchElementException();
560 return x;
561 }
562
563 public E peekFirst() {
564 final ReentrantLock lock = this.lock;
565 lock.lock();
566 try {
567 return (first == null) ? null : first.item;
568 } finally {
569 lock.unlock();
570 }
571 }
572
573 public E peekLast() {
574 final ReentrantLock lock = this.lock;
575 lock.lock();
576 try {
577 return (last == null) ? null : last.item;
578 } finally {
579 lock.unlock();
580 }
581 }
582
583 public boolean removeFirstOccurrence(Object o) {
584 if (o == null) return false;
585 final ReentrantLock lock = this.lock;
586 lock.lock();
587 try {
588 for (Node<E> p = first; p != null; p = p.next) {
589 if (o.equals(p.item)) {
590 unlink(p);
591 return true;
592 }
593 }
594 return false;
595 } finally {
596 lock.unlock();
597 }
598 }
599
600 public boolean removeLastOccurrence(Object o) {
601 if (o == null) return false;
602 final ReentrantLock lock = this.lock;
603 lock.lock();
604 try {
605 for (Node<E> p = last; p != null; p = p.prev) {
606 if (o.equals(p.item)) {
607 unlink(p);
608 return true;
609 }
610 }
611 return false;
612 } finally {
613 lock.unlock();
614 }
615 }
616
617 // BlockingQueue methods
618
619 /**
620 * Inserts the specified element at the end of this deque unless it would
621 * violate capacity restrictions. When using a capacity-restricted deque,
622 * it is generally preferable to use method {@link #offer(Object) offer}.
623 *
624 * <p>This method is equivalent to {@link #addLast}.
625 *
626 * @throws IllegalStateException if the element cannot be added at this
627 * time due to capacity restrictions
628 * @throws NullPointerException if the specified element is null
629 */
630 public boolean add(E e) {
631 addLast(e);
632 return true;
633 }
634
635 /**
636 * @throws NullPointerException if the specified element is null
637 */
638 public boolean offer(E e) {
639 return offerLast(e);
640 }
641
642 /**
643 * @throws NullPointerException {@inheritDoc}
644 * @throws InterruptedException {@inheritDoc}
645 */
646 public void put(E e) throws InterruptedException {
647 putLast(e);
648 }
649
650 /**
651 * @throws NullPointerException {@inheritDoc}
652 * @throws InterruptedException {@inheritDoc}
653 */
654 public boolean offer(E e, long timeout, TimeUnit unit)
655 throws InterruptedException {
656 return offerLast(e, timeout, unit);
657 }
658
659 /**
660 * Retrieves and removes the head of the queue represented by this deque.
661 * This method differs from {@link #poll poll} only in that it throws an
662 * exception if this deque is empty.
663 *
664 * <p>This method is equivalent to {@link #removeFirst() removeFirst}.
665 *
666 * @return the head of the queue represented by this deque
667 * @throws NoSuchElementException if this deque is empty
668 */
669 public E remove() {
670 return removeFirst();
671 }
672
673 public E poll() {
674 return pollFirst();
675 }
676
677 public E take() throws InterruptedException {
678 return takeFirst();
679 }
680
681 public E poll(long timeout, TimeUnit unit) throws InterruptedException {
682 return pollFirst(timeout, unit);
683 }
684
685 /**
686 * Retrieves, but does not remove, the head of the queue represented by
687 * this deque. This method differs from {@link #peek peek} only in that
688 * it throws an exception if this deque is empty.
689 *
690 * <p>This method is equivalent to {@link #getFirst() getFirst}.
691 *
692 * @return the head of the queue represented by this deque
693 * @throws NoSuchElementException if this deque is empty
694 */
695 public E element() {
696 return getFirst();
697 }
698
699 public E peek() {
700 return peekFirst();
701 }
702
703 /**
704 * Returns the number of additional elements that this deque can ideally
705 * (in the absence of memory or resource constraints) accept without
706 * blocking. This is always equal to the initial capacity of this deque
707 * less the current {@code size} of this deque.
708 *
709 * <p>Note that you <em>cannot</em> always tell if an attempt to insert
710 * an element will succeed by inspecting {@code remainingCapacity}
711 * because it may be the case that another thread is about to
712 * insert or remove an element.
713 */
714 public int remainingCapacity() {
715 final ReentrantLock lock = this.lock;
716 lock.lock();
717 try {
718 return capacity - count;
719 } finally {
720 lock.unlock();
721 }
722 }
723
724 /**
725 * @throws UnsupportedOperationException {@inheritDoc}
726 * @throws ClassCastException {@inheritDoc}
727 * @throws NullPointerException {@inheritDoc}
728 * @throws IllegalArgumentException {@inheritDoc}
729 */
730 public int drainTo(Collection<? super E> c) {
731 return drainTo(c, Integer.MAX_VALUE);
732 }
733
734 /**
735 * @throws UnsupportedOperationException {@inheritDoc}
736 * @throws ClassCastException {@inheritDoc}
737 * @throws NullPointerException {@inheritDoc}
738 * @throws IllegalArgumentException {@inheritDoc}
739 */
740 public int drainTo(Collection<? super E> c, int maxElements) {
741 if (c == null)
742 throw new NullPointerException();
743 if (c == this)
744 throw new IllegalArgumentException();
745 final ReentrantLock lock = this.lock;
746 lock.lock();
747 try {
748 int n = Math.min(maxElements, count);
749 for (int i = 0; i < n; i++) {
750 c.add(first.item); // In this order, in case add() throws.
751 unlinkFirst();
752 }
753 return n;
754 } finally {
755 lock.unlock();
756 }
757 }
758
759 // Stack methods
760
761 /**
762 * @throws IllegalStateException {@inheritDoc}
763 * @throws NullPointerException {@inheritDoc}
764 */
765 public void push(E e) {
766 addFirst(e);
767 }
768
769 /**
770 * @throws NoSuchElementException {@inheritDoc}
771 */
772 public E pop() {
773 return removeFirst();
774 }
775
776 // Collection methods
777
778 /**
779 * Removes the first occurrence of the specified element from this deque.
780 * If the deque does not contain the element, it is unchanged.
781 * More formally, removes the first element {@code e} such that
782 * {@code o.equals(e)} (if such an element exists).
783 * Returns {@code true} if this deque contained the specified element
784 * (or equivalently, if this deque changed as a result of the call).
785 *
786 * <p>This method is equivalent to
787 * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
788 *
789 * @param o element to be removed from this deque, if present
790 * @return {@code true} if this deque changed as a result of the call
791 */
792 public boolean remove(Object o) {
793 return removeFirstOccurrence(o);
794 }
795
796 /**
797 * Returns the number of elements in this deque.
798 *
799 * @return the number of elements in this deque
800 */
801 public int size() {
802 final ReentrantLock lock = this.lock;
803 lock.lock();
804 try {
805 return count;
806 } finally {
807 lock.unlock();
808 }
809 }
810
811 /**
812 * Returns {@code true} if this deque contains the specified element.
813 * More formally, returns {@code true} if and only if this deque contains
814 * at least one element {@code e} such that {@code o.equals(e)}.
815 *
816 * @param o object to be checked for containment in this deque
817 * @return {@code true} if this deque contains the specified element
818 */
819 public boolean contains(Object o) {
820 if (o == null) return false;
821 final ReentrantLock lock = this.lock;
822 lock.lock();
823 try {
824 for (Node<E> p = first; p != null; p = p.next)
825 if (o.equals(p.item))
826 return true;
827 return false;
828 } finally {
829 lock.unlock();
830 }
831 }
832
833 /*
834 * TODO: Add support for more efficient bulk operations.
835 *
836 * We don't want to acquire the lock for every iteration, but we
837 * also want other threads a chance to interact with the
838 * collection, especially when count is close to capacity.
839 */
840
841 // /**
842 // * Adds all of the elements in the specified collection to this
843 // * queue. Attempts to addAll of a queue to itself result in
844 // * {@code IllegalArgumentException}. Further, the behavior of
845 // * this operation is undefined if the specified collection is
846 // * modified while the operation is in progress.
847 // *
848 // * @param c collection containing elements to be added to this queue
849 // * @return {@code true} if this queue changed as a result of the call
850 // * @throws ClassCastException {@inheritDoc}
851 // * @throws NullPointerException {@inheritDoc}
852 // * @throws IllegalArgumentException {@inheritDoc}
853 // * @throws IllegalStateException {@inheritDoc}
854 // * @see #add(Object)
855 // */
856 // public boolean addAll(Collection<? extends E> c) {
857 // if (c == null)
858 // throw new NullPointerException();
859 // if (c == this)
860 // throw new IllegalArgumentException();
861 // final ReentrantLock lock = this.lock;
862 // lock.lock();
863 // try {
864 // boolean modified = false;
865 // for (E e : c)
866 // if (linkLast(e))
867 // modified = true;
868 // return modified;
869 // } finally {
870 // lock.unlock();
871 // }
872 // }
873
874 /**
875 * Returns an array containing all of the elements in this deque, in
876 * proper sequence (from first to last element).
877 *
878 * <p>The returned array will be "safe" in that no references to it are
879 * maintained by this deque. (In other words, this method must allocate
880 * a new array). The caller is thus free to modify the returned array.
881 *
882 * <p>This method acts as bridge between array-based and collection-based
883 * APIs.
884 *
885 * @return an array containing all of the elements in this deque
886 */
887 @SuppressWarnings("unchecked")
888 public Object[] toArray() {
889 final ReentrantLock lock = this.lock;
890 lock.lock();
891 try {
892 Object[] a = new Object[count];
893 int k = 0;
894 for (Node<E> p = first; p != null; p = p.next)
895 a[k++] = p.item;
896 return a;
897 } finally {
898 lock.unlock();
899 }
900 }
901
902 /**
903 * Returns an array containing all of the elements in this deque, in
904 * proper sequence; the runtime type of the returned array is that of
905 * the specified array. If the deque fits in the specified array, it
906 * is returned therein. Otherwise, a new array is allocated with the
907 * runtime type of the specified array and the size of this deque.
908 *
909 * <p>If this deque fits in the specified array with room to spare
910 * (i.e., the array has more elements than this deque), the element in
911 * the array immediately following the end of the deque is set to
912 * {@code null}.
913 *
914 * <p>Like the {@link #toArray()} method, this method acts as bridge between
915 * array-based and collection-based APIs. Further, this method allows
916 * precise control over the runtime type of the output array, and may,
917 * under certain circumstances, be used to save allocation costs.
918 *
919 * <p>Suppose {@code x} is a deque known to contain only strings.
920 * The following code can be used to dump the deque into a newly
921 * allocated array of {@code String}:
922 *
923 * <pre>
924 * String[] y = x.toArray(new String[0]);</pre>
925 *
926 * Note that {@code toArray(new Object[0])} is identical in function to
927 * {@code toArray()}.
928 *
929 * @param a the array into which the elements of the deque are to
930 * be stored, if it is big enough; otherwise, a new array of the
931 * same runtime type is allocated for this purpose
932 * @return an array containing all of the elements in this deque
933 * @throws ArrayStoreException if the runtime type of the specified array
934 * is not a supertype of the runtime type of every element in
935 * this deque
936 * @throws NullPointerException if the specified array is null
937 */
938 @SuppressWarnings("unchecked")
939 public <T> T[] toArray(T[] a) {
940 final ReentrantLock lock = this.lock;
941 lock.lock();
942 try {
943 if (a.length < count)
944 a = (T[])java.lang.reflect.Array.newInstance
945 (a.getClass().getComponentType(), count);
946
947 int k = 0;
948 for (Node<E> p = first; p != null; p = p.next)
949 a[k++] = (T)p.item;
950 if (a.length > k)
951 a[k] = null;
952 return a;
953 } finally {
954 lock.unlock();
955 }
956 }
957
958 public String toString() {
959 final ReentrantLock lock = this.lock;
960 lock.lock();
961 try {
962 Node<E> p = first;
963 if (p == null)
964 return "[]";
965
966 StringBuilder sb = new StringBuilder();
967 sb.append('[');
968 for (;;) {
969 E e = p.item;
970 sb.append(e == this ? "(this Collection)" : e);
971 p = p.next;
972 if (p == null)
973 return sb.append(']').toString();
974 sb.append(',').append(' ');
975 }
976 } finally {
977 lock.unlock();
978 }
979 }
980
981 /**
982 * Atomically removes all of the elements from this deque.
983 * The deque will be empty after this call returns.
984 */
985 public void clear() {
986 final ReentrantLock lock = this.lock;
987 lock.lock();
988 try {
989 for (Node<E> f = first; f != null; ) {
990 f.item = null;
991 Node<E> n = f.next;
992 f.prev = null;
993 f.next = null;
994 f = n;
995 }
996 first = last = null;
997 count = 0;
998 notFull.signalAll();
999 } finally {
1000 lock.unlock();
1001 }
1002 }
1003
1004 /**
1005 * Returns an iterator over the elements in this deque in proper sequence.
1006 * The elements will be returned in order from first (head) to last (tail).
1007 *
1008 * <p>The returned iterator is a "weakly consistent" iterator that
1009 * will never throw {@link java.util.ConcurrentModificationException
1010 * ConcurrentModificationException}, and guarantees to traverse
1011 * elements as they existed upon construction of the iterator, and
1012 * may (but is not guaranteed to) reflect any modifications
1013 * subsequent to construction.
1014 *
1015 * @return an iterator over the elements in this deque in proper sequence
1016 */
1017 public Iterator<E> iterator() {
1018 return new Itr();
1019 }
1020
1021 /**
1022 * Returns an iterator over the elements in this deque in reverse
1023 * sequential order. The elements will be returned in order from
1024 * last (tail) to first (head).
1025 *
1026 * <p>The returned iterator is a "weakly consistent" iterator that
1027 * will never throw {@link java.util.ConcurrentModificationException
1028 * ConcurrentModificationException}, and guarantees to traverse
1029 * elements as they existed upon construction of the iterator, and
1030 * may (but is not guaranteed to) reflect any modifications
1031 * subsequent to construction.
1032 *
1033 * @return an iterator over the elements in this deque in reverse order
1034 */
1035 public Iterator<E> descendingIterator() {
1036 return new DescendingItr();
1037 }
1038
1039 /**
1040 * Base class for Iterators for LinkedBlockingDeque
1041 */
1042 private abstract class AbstractItr implements Iterator<E> {
1043 /**
1044 * The next node to return in next()
1045 */
1046 Node<E> next;
1047
1048 /**
1049 * nextItem holds on to item fields because once we claim that
1050 * an element exists in hasNext(), we must return item read
1051 * under lock (in advance()) even if it was in the process of
1052 * being removed when hasNext() was called.
1053 */
1054 E nextItem;
1055
1056 /**
1057 * Node returned by most recent call to next. Needed by remove.
1058 * Reset to null if this element is deleted by a call to remove.
1059 */
1060 private Node<E> lastRet;
1061
1062 abstract Node<E> firstNode();
1063 abstract Node<E> nextNode(Node<E> n);
1064
1065 AbstractItr() {
1066 // set to initial position
1067 final ReentrantLock lock = LinkedBlockingDeque.this.lock;
1068 lock.lock();
1069 try {
1070 next = firstNode();
1071 nextItem = (next == null) ? null : next.item;
1072 } finally {
1073 lock.unlock();
1074 }
1075 }
1076
1077 /**
1078 * Returns the successor node of the given non-null, but
1079 * possibly previously deleted, node.
1080 */
1081 private Node<E> succ(Node<E> n) {
1082 // Chains of deleted nodes ending in null or self-links
1083 // are possible if multiple interior nodes are removed.
1084 for (;;) {
1085 Node<E> s = nextNode(n);
1086 if (s == null)
1087 return null;
1088 else if (s.item != null)
1089 return s;
1090 else if (s == n)
1091 return firstNode();
1092 else
1093 n = s;
1094 }
1095 }
1096
1097 /**
1098 * Advances next.
1099 */
1100 void advance() {
1101 final ReentrantLock lock = LinkedBlockingDeque.this.lock;
1102 lock.lock();
1103 try {
1104 // assert next != null;
1105 next = succ(next);
1106 nextItem = (next == null) ? null : next.item;
1107 } finally {
1108 lock.unlock();
1109 }
1110 }
1111
1112 public boolean hasNext() {
1113 return next != null;
1114 }
1115
1116 public E next() {
1117 if (next == null)
1118 throw new NoSuchElementException();
1119 lastRet = next;
1120 E x = nextItem;
1121 advance();
1122 return x;
1123 }
1124
1125 public void remove() {
1126 Node<E> n = lastRet;
1127 if (n == null)
1128 throw new IllegalStateException();
1129 lastRet = null;
1130 final ReentrantLock lock = LinkedBlockingDeque.this.lock;
1131 lock.lock();
1132 try {
1133 if (n.item != null)
1134 unlink(n);
1135 } finally {
1136 lock.unlock();
1137 }
1138 }
1139 }
1140
1141 /** Forward iterator */
1142 private class Itr extends AbstractItr {
1143 Node<E> firstNode() { return first; }
1144 Node<E> nextNode(Node<E> n) { return n.next; }
1145 }
1146
1147 /** Descending iterator */
1148 private class DescendingItr extends AbstractItr {
1149 Node<E> firstNode() { return last; }
1150 Node<E> nextNode(Node<E> n) { return n.prev; }
1151 }
1152
1153 /**
1154 * Save the state of this deque to a stream (that is, serialize it).
1155 *
1156 * @serialData The capacity (int), followed by elements (each an
1157 * {@code Object}) in the proper order, followed by a null
1158 * @param s the stream
1159 */
1160 private void writeObject(java.io.ObjectOutputStream s)
1161 throws java.io.IOException {
1162 final ReentrantLock lock = this.lock;
1163 lock.lock();
1164 try {
1165 // Write out capacity and any hidden stuff
1166 s.defaultWriteObject();
1167 // Write out all elements in the proper order.
1168 for (Node<E> p = first; p != null; p = p.next)
1169 s.writeObject(p.item);
1170 // Use trailing null as sentinel
1171 s.writeObject(null);
1172 } finally {
1173 lock.unlock();
1174 }
1175 }
1176
1177 /**
1178 * Reconstitute this deque from a stream (that is,
1179 * deserialize it).
1180 * @param s the stream
1181 */
1182 private void readObject(java.io.ObjectInputStream s)
1183 throws java.io.IOException, ClassNotFoundException {
1184 s.defaultReadObject();
1185 count = 0;
1186 first = null;
1187 last = null;
1188 // Read in all elements and place in queue
1189 for (;;) {
1190 @SuppressWarnings("unchecked")
1191 E item = (E)s.readObject();
1192 if (item == null)
1193 break;
1194 add(item);
1195 }
1196 }
1197
1198 }
下面从ArrayBlockingQueue的创建,添加,取出,遍历这几个方面对LinkedBlockingDeque进行分析
1. 创建
下面以LinkedBlockingDeque(int capacity)来进行说明。
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
说明:capacity是“链式阻塞队列”的容量。
LinkedBlockingDeque中相关的数据结果定义如下:
// “双向队列”的表头
transient Node<E> first;
// “双向队列”的表尾
transient Node<E> last;
// 节点数量
private transient int count;
// 容量
private final int capacity;
// 互斥锁 , 互斥锁对应的“非空条件notEmpty”, 互斥锁对应的“未满条件notFull”
final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
说明:lock是互斥锁,用于控制多线程对LinkedBlockingDeque中元素的互斥访问;而notEmpty和notFull是与lock绑定的条件,它们用于实现对多线程更精确的控制。
双向链表的节点Node的定义如下:
static final class Node<E> {
E item; // 数据
Node<E> prev; // 前一节点
Node<E> next; // 后一节点
Node(E x) { item = x; }
}
2. 添加
下面以offer(E e)为例,对LinkedBlockingDeque的添加方法进行说明。
public boolean offer(E e) {
return offerLast(e);
}
offer()实际上是调用offerLast()将元素添加到队列的末尾。
offerLast()的源码如下:
public boolean offerLast(E e) {
if (e == null) throw new NullPointerException();
// 新建节点
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
// 获取锁
lock.lock();
try {
// 将“新节点”添加到双向链表的末尾
return linkLast(node);
} finally {
// 释放锁
lock.unlock();
}
}
说明:offerLast()的作用,是新建节点并将该节点插入到双向链表的末尾。它在插入节点前,会获取锁;操作完毕,再释放锁。
linkLast()的源码如下:
private boolean linkLast(Node<E> node) {
// 如果“双向链表的节点数量” > “容量”,则返回false,表示插入失败。
if (count >= capacity)
return false;
// 将“node添加到链表末尾”,并设置node为新的尾节点
Node<E> l = last;
node.prev = l;
last = node;
if (first == null)
first = node;
else
l.next = node;
// 将“节点数量”+1
++count;
// 插入节点之后,唤醒notEmpty上的等待线程。
notEmpty.signal();
return true;
}
说明:linkLast()的作用,是将节点插入到双向队列的末尾;插入节点之后,唤醒notEmpty上的等待线程。
3. 删除
下面以take()为例,对LinkedBlockingDeque的取出方法进行说明。
public E take() throws InterruptedException {
return takeFirst();
}
take()实际上是调用takeFirst()队列的第一个元素。
takeFirst()的源码如下:
public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 获取锁
lock.lock();
try {
E x;
// 若“队列为空”,则一直等待。否则,通过unlinkFirst()删除第一个节点。
while ( (x = unlinkFirst()) == null)
notEmpty.await();
return x;
} finally {
// 释放锁
lock.unlock();
}
}
说明:takeFirst()的作用,是删除双向链表的第一个节点,并返回节点对应的值。它在插入节点前,会获取锁;操作完毕,再释放锁。
unlinkFirst()的源码如下:
private E unlinkFirst() {
// assert lock.isHeldByCurrentThread();
Node<E> f = first;
if (f == null)
return null;
// 删除并更新“第一个节点”
Node<E> n = f.next;
E item = f.item;
f.item = null;
f.next = f; // help GC
first = n;
if (n == null)
last = null;
else
n.prev = null;
// 将“节点数量”-1
--count;
// 删除节点之后,唤醒notFull上的等待线程。
notFull.signal();
return item;
}
说明:unlinkFirst()的作用,是将双向队列的第一个节点删除;删除节点之后,唤醒notFull上的等待线程。
4. 遍历
下面对LinkedBlockingDeque的遍历方法进行说明。
public Iterator<E> iterator() {
return new Itr();
}
iterator()实际上是返回一个Iter对象。
Itr类的定义如下:
private class Itr extends AbstractItr {
// “双向队列”的表头
Node<E> firstNode() { return first; }
// 获取“节点n的下一个节点”
Node<E> nextNode(Node<E> n) { return n.next; }
}
Itr继承于AbstractItr,而AbstractItr的定义如下:
private abstract class AbstractItr implements Iterator<E> {
// next是下一次调用next()会返回的节点。
Node<E> next;
// nextItem是next()返回节点对应的数据。
E nextItem;
// 上一次next()返回的节点。
private Node<E> lastRet;
// 返回第一个节点
abstract Node<E> firstNode();
// 返回下一个节点
abstract Node<E> nextNode(Node<E> n);
AbstractItr() {
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
// 获取“LinkedBlockingDeque的互斥锁”
lock.lock();
try {
// 获取“双向队列”的表头
next = firstNode();
// 获取表头对应的数据
nextItem = (next == null) ? null : next.item;
} finally {
// 释放“LinkedBlockingDeque的互斥锁”
lock.unlock();
}
}
// 获取n的后继节点
private Node<E> succ(Node<E> n) {
// Chains of deleted nodes ending in null or self-links
// are possible if multiple interior nodes are removed.
for (;;) {
Node<E> s = nextNode(n);
if (s == null)
return null;
else if (s.item != null)
return s;
else if (s == n)
return firstNode();
else
n = s;
}
}
// 更新next和nextItem。
void advance() {
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
// assert next != null;
next = succ(next);
nextItem = (next == null) ? null : next.item;
} finally {
lock.unlock();
}
}
// 返回“下一个节点是否为null”
public boolean hasNext() {
return next != null;
}
// 返回下一个节点
public E next() {
if (next == null)
throw new NoSuchElementException();
lastRet = next;
E x = nextItem;
advance();
return x;
}
// 删除下一个节点
public void remove() {
Node<E> n = lastRet;
if (n == null)
throw new IllegalStateException();
lastRet = null;
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
if (n.item != null)
unlink(n);
} finally {
lock.unlock();
}
}
}
LinkedBlockingDeque示例
1 import java.util.*;
2 import java.util.concurrent.*;
3
4 /*
5 * LinkedBlockingDeque是“线程安全”的队列,而LinkedList是非线程安全的。
6 *
7 * 下面是“多个线程同时操作并且遍历queue”的示例
8 * (01) 当queue是LinkedBlockingDeque对象时,程序能正常运行。
9 * (02) 当queue是LinkedList对象时,程序会产生ConcurrentModificationException异常。
10 *
11 * @author skywang
12 */
13 public class LinkedBlockingDequeDemo1 {
14
15 // TODO: queue是LinkedList对象时,程序会出错。
16 //private static Queue<String> queue = new LinkedList<String>();
17 private static Queue<String> queue = new LinkedBlockingDeque<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 }
(某一次)运行结果:
ta1, ta1, tb1, tb1,
ta1, ta1, tb1, tb1, tb2, tb2, ta2,
ta2,
ta1, ta1, tb1, tb1, tb2, tb2, ta2, ta2, tb3, tb3, ta3,
ta3, ta1,
tb1, ta1, tb2, tb1, ta2, tb2, tb3, ta2, ta3, tb3, tb4, ta3, ta4,
tb4, ta1, ta4, tb1, tb5,
tb2, ta1, ta2, tb1, tb3, tb2, ta3, ta2, tb4, tb3, ta4, ta3, tb5, tb4, ta5,
ta4, ta1, tb5, tb1, ta5, tb2, tb6,
ta2, ta1, tb3, tb1, ta3, tb2, tb4, ta2, ta4, tb3, tb5, ta3, ta5, tb4, tb6, ta4, ta6,
tb5, ta5, tb6, ta6,
结果说明:示例程序中,启动两个线程(线程ta和线程tb)分别对LinkedBlockingDeque进行操作。以线程ta而言,它会先获取“线程名”+“序号”,然后将该字符串添加到LinkedBlockingDeque中;接着,遍历并输出LinkedBlockingDeque中的全部元素。 线程tb的操作和线程ta一样,只不过线程tb的名字和线程ta的名字不同。
当queue是LinkedBlockingDeque对象时,程序能正常运行。如果将queue改为LinkedList时,程序会产生ConcurrentModificationException异常。
更多内容