有大小的队列就叫有界队列 如 ArrayBlockingquue, 反之是无界队列 如 LinkedBlockingDeque。 
单词写错了。
 是的,LinkedBlockingDeque 永远满不了了,但内存用完了,程序就崩了。
 
 

一、双向队列Deque

Queue除了前面介绍的实现外,还有一种双向的Queue实现Deque。这种队列允许在队列头和尾部进行入队出队操作,因此在功能上比Queue显然要更复杂。下图描述的是Deque的完整体系图。需要说明的是LinkedList也已经加入了Deque的一部分(LinkedList是从jdk1.2
开始就存在数据结构)。

Deque在Queue的基础上增加了更多的操作方法。

从上图可以看到,Deque不仅具有FIFO的Queue实现,也有FILO的实现,也就是不仅可以实现队列,也可以实现一个堆栈。

同时在Deque的体系结构图中可以看到,实现一个Deque可以使用数组(ArrayDeque),同时也可以使用链表(LinkedList),还可以同实现一个支持阻塞的线程安全版本队列LinkedBlockingDeque。

1、ArrayDeque实现Deque

对于数组实现的Deque来说,数据结构上比较简单,只需要一个存储数据的数组以及头尾两个索引即可。由于数组是固定长度的,所以很容易就得到数组的头和尾,那么对于数组的操作只需要移动头和尾的索引即可。

特别说明的是ArrayDeque并不是一个固定大小的队列,每次队列满了以后就将队列容量扩大一倍(doubleCapacity()),因此加入一个元素总是能成功,而且也不会抛出一个异常。也就是说ArrayDeque是一个没有容量限制的队列。

同样继续性能的考虑,使用System.arraycopy复制一个数组比循环设置要高效得多。

1.1、ArrayDeque的源码解析

  1. //数组双端队列ArrayDeque的源码解析
  2. public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable{
  3. /**
  4. * 存放队列元素的数组,数组的长度为“2的指数”
  5. */
  6. private transient E[] elements;
  7. /**
  8. *队列的头部索引位置,(被remove()或pop()操作的位置),当为空队列时,首尾index相同
  9. */
  10. private transient int head;
  11. /**
  12. * 队列的尾部索引位置,(被 addLast(E), add(E), 或 push(E)操作的位置).
  13. */
  14. private transient int tail;
  15. /**
  16. * 队列的最小容量(大小必须为“2的指数”)
  17. */
  18. private static final int MIN_INITIAL_CAPACITY = 8;
  19. // ******  Array allocation and resizing utilities ******
  20. /**
  21. * 根据所给的数组长度,得到一个比该长度大的最小的2^p的真实长度,并建立真实长度的空数组
  22. */
  23. private void allocateElements(int numElements) {
  24. int initialCapacity = MIN_INITIAL_CAPACITY;
  25. if (numElements >= initialCapacity) {
  26. initialCapacity = numElements;
  27. initialCapacity |= (initialCapacity >>>  1);
  28. initialCapacity |= (initialCapacity >>>  2);
  29. initialCapacity |= (initialCapacity >>>  4);
  30. initialCapacity |= (initialCapacity >>>  8);
  31. initialCapacity |= (initialCapacity >>> 16);
  32. initialCapacity++;
  33. if (initialCapacity < 0)   // Too many elements, must back off
  34. initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
  35. }
  36. elements = (E[]) new Object[initialCapacity];
  37. }
  38. /**
  39. * 当队列首尾指向同一个引用时,扩充队列的容量为原来的两倍,并对元素重新定位到新数组中
  40. */
  41. private void doubleCapacity() {
  42. assert head == tail;
  43. int p = head;
  44. int n = elements.length;
  45. int r = n - p; // number of elements to the right of p
  46. int newCapacity = n << 1;
  47. if (newCapacity < 0)
  48. throw new IllegalStateException("Sorry, deque too big");
  49. Object[] a = new Object[newCapacity];
  50. System.arraycopy(elements, p, a, 0, r);
  51. System.arraycopy(elements, 0, a, r, p);
  52. elements = (E[])a;
  53. head = 0;
  54. tail = n;
  55. }
  56. /**
  57. * 拷贝队列中的元素到新数组中
  58. */
  59. private <T> T[] copyElements(T[] a) {
  60. if (head < tail) {
  61. System.arraycopy(elements, head, a, 0, size());
  62. } else if (head > tail) {
  63. int headPortionLen = elements.length - head;
  64. System.arraycopy(elements, head, a, 0, headPortionLen);
  65. System.arraycopy(elements, 0, a, headPortionLen, tail);
  66. }
  67. return a;
  68. }
  69. /**
  70. * 默认构造队列,初始化一个长度为16的数组
  71. */
  72. public ArrayDeque() {
  73. elements = (E[]) new Object[16];
  74. }
  75. /**
  76. * 指定元素个数的构造方法
  77. */
  78. public ArrayDeque(int numElements) {
  79. allocateElements(numElements);
  80. }
  81. /**
  82. * 用一个集合作为参数的构造方法
  83. */
  84. public ArrayDeque(Collection<? extends E> c) {
  85. allocateElements(c.size());
  86. addAll(c);
  87. }
  88. //插入和删除的方法主要是: addFirst(),addLast(), pollFirst(), pollLast()。
  89. //其他的方法依赖于这些实现。
  90. /**
  91. * 在双端队列的前端插入元素,元素为null抛异常
  92. */
  93. public void addFirst(E e) {
  94. if (e == null)
  95. throw new NullPointerException();
  96. elements[head = (head - 1) & (elements.length - 1)] = e;
  97. if (head == tail)
  98. doubleCapacity();
  99. }
  100. /**
  101. *在双端队列的末端插入元素,元素为null抛异常
  102. */
  103. public void addLast(E e) {
  104. if (e == null)
  105. throw new NullPointerException();
  106. elements[tail] = e;
  107. if ( (tail = (tail + 1) & (elements.length - 1)) == head)
  108. doubleCapacity();
  109. }
  110. /**
  111. * 在前端插入,调用addFirst实现,返回boolean类型
  112. */
  113. public boolean offerFirst(E e) {
  114. addFirst(e);
  115. return true;
  116. }
  117. /**
  118. * 在末端插入,调用addLast实现,返回boolean类型
  119. */
  120. public boolean offerLast(E e) {
  121. addLast(e);
  122. return true;
  123. }
  124. /**
  125. * 删除前端,调用pollFirst实现
  126. */
  127. public E removeFirst() {
  128. E x = pollFirst();
  129. if (x == null)
  130. throw new NoSuchElementException();
  131. return x;
  132. }
  133. /**
  134. * 删除后端,调用pollLast实现
  135. */
  136. public E removeLast() {
  137. E x = pollLast();
  138. if (x == null)
  139. throw new NoSuchElementException();
  140. return x;
  141. }
  142. //前端出对(删除前端)
  143. public E pollFirst() {
  144. int h = head;
  145. E result = elements[h]; // Element is null if deque empty
  146. if (result == null)
  147. return null;
  148. elements[h] = null;     // Must null out slot
  149. head = (h + 1) & (elements.length - 1);
  150. return result;
  151. }
  152. //后端出对(删除后端)
  153. public E pollLast() {
  154. int t = (tail - 1) & (elements.length - 1);
  155. E result = elements[t];
  156. if (result == null)
  157. return null;
  158. elements[t] = null;
  159. tail = t;
  160. return result;
  161. }
  162. /**
  163. * 得到前端头元素
  164. */
  165. public E getFirst() {
  166. E x = elements[head];
  167. if (x == null)
  168. throw new NoSuchElementException();
  169. return x;
  170. }
  171. /**
  172. * 得到末端尾元素
  173. */
  174. public E getLast() {
  175. E x = elements[(tail - 1) & (elements.length - 1)];
  176. if (x == null)
  177. throw new NoSuchElementException();
  178. return x;
  179. }
  180. public E peekFirst() {
  181. return elements[head]; // elements[head] is null if deque empty
  182. }
  183. public E peekLast() {
  184. return elements[(tail - 1) & (elements.length - 1)];
  185. }
  186. /**
  187. * 移除此双端队列中第一次出现的指定元素(当从头部到尾部遍历双端队列时)。
  188. */
  189. public boolean removeFirstOccurrence(Object o) {
  190. if (o == null)
  191. return false;
  192. int mask = elements.length - 1;
  193. int i = head;
  194. E x;
  195. while ( (x = elements[i]) != null) {
  196. if (o.equals(x)) {
  197. delete(i);
  198. return true;
  199. }
  200. i = (i + 1) & mask;
  201. }
  202. return false;
  203. }
  204. /**
  205. * 移除此双端队列中最后一次出现的指定元素(当从头部到尾部遍历双端队列时)。
  206. */
  207. public boolean removeLastOccurrence(Object o) {
  208. if (o == null)
  209. return false;
  210. int mask = elements.length - 1;
  211. int i = (tail - 1) & mask;
  212. E x;
  213. while ( (x = elements[i]) != null) {
  214. if (o.equals(x)) {
  215. delete(i);
  216. return true;
  217. }
  218. i = (i - 1) & mask;
  219. }
  220. return false;
  221. }
  222. // *** 队列方法(Queue methods) ***
  223. /**
  224. * add方法,添加到队列末端
  225. */
  226. public boolean add(E e) {
  227. addLast(e);
  228. return true;
  229. }
  230. /**
  231. * 同上
  232. */
  233. public boolean offer(E e) {
  234. return offerLast(e);
  235. }
  236. /**
  237. * remove元素,删除队列前端
  238. */
  239. public E remove() {
  240. return removeFirst();
  241. }
  242. /**
  243. * 弹出前端(出对,删除前端)
  244. */
  245. public E poll() {
  246. return pollFirst();
  247. }
  248. public E element() {
  249. return getFirst();
  250. }
  251. public E peek() {
  252. return peekFirst();
  253. }
  254. // *** 栈 方法(Stack methods) ***
  255. public void push(E e) {
  256. addFirst(e);
  257. }
  258. public E pop() {
  259. return removeFirst();
  260. }
  261. private void checkInvariants() { ……    }
  262. private boolean delete(int i) {   ……   }
  263. // *** 集合方法(Collection Methods) ***
  264. ……
  265. // *** Object methods ***
  266. ……
  267. }
  268. 整体来说:1个数组,2个index(head 索引和tail索引)。实现比较简单,容易理解。

2、LinkedList实现Deque

对于LinkedList本身而言,数据结构就更简单了,除了一个size用来记录大小外,只有head一个元素Entry。对比Map和Queue的其它数据结构可以看到这里的Entry有两个引用,是双向的队列。

在示意图中,LinkedList总是有一个“傀儡”节点,用来描述队列“头部”,但是并不表示头部元素,它是一个执行null的空节点。

队列一开始只有head一个空元素,然后从尾部加入E1(add/addLast),head和E1之间建立双向链接。然后继续从尾部加入E2,E2就在head和E1之间建立双向链接。最后从队列的头部加入E3(push/addFirst),于是E3就在E1和head之间链接双向链接。

双向链表的数据结构比较简单,操作起来也比较容易,从事从“傀儡”节点开始,“傀儡”节点的下一个元素就是队列的头部,前一个元素是队列的尾部,换句话说,“傀儡”节点在头部和尾部之间建立了一个通道,是整个队列形成一个循环,这样就可以从任意一个节点的任意一个方向能遍历完整的队列。

同样LinkedList也是一个没有容量限制的队列,因此入队列(不管是从头部还是尾部)总能成功。

3、小结

上面描述的ArrayDeque和LinkedList是两种不同方式的实现,通常在遍历和节省内存上ArrayDeque更高效(索引更快,另外不需要Entry对象),但是在队列扩容下LinkedList更灵活,因为不需要复制原始的队列,某些情况下可能更高效。

同样需要注意的上述两个实现都不是线程安全的,因此只适合在单线程环境下使用,下面章节要介绍的LinkedBlockingDeque就是线程安全的可阻塞的Deque。事实上也应该是功能最强大的Queue实现,当然了实现起来也许会复杂一点。

二、双向并发阻塞队列 LinkedBlockingDeque

1、LinkedBlockingDeque数据结构

双向并发阻塞队列。所谓双向是指可以从队列的头和尾同时操作,并发只是线程安全的实现,阻塞允许在入队出队不满足条件时挂起线程,这里说的队列是指支持FIFO/FILO实现的链表。

首先看下LinkedBlockingDeque的数据结构。通常情况下从数据结构上就能看出这种实现的优缺点,这样就知道如何更好的使用工具了。

从数据结构和功能需求上可以得到以下结论:

  1. 要想支持阻塞功能,队列的容量一定是固定的,否则无法在入队的时候挂起线程。也就是capacity是final类型的。
  2. 既然是双向链表,每一个结点就需要前后两个引用,这样才能将所有元素串联起来,支持双向遍历。也即需要prev/next两个引用。
  3. 双向链表需要头尾同时操作,所以需要first/last两个节点,当然可以参考LinkedList那样采用一个节点的双向来完成,那样实现起来就稍微麻烦点。
  4. 既然要支持阻塞功能,就需要锁和条件变量来挂起线程。这里使用一个锁两个条件变量来完成此功能。

2、LinkedBlockingDeque源码分析

  1. public class LinkedBlockingDeque<E> extends AbstractQueue<E> implements BlockingDeque<E>,  java.io.Serializable {
  2. /** 包含前驱和后继节点的双向链式结构 */
  3. static final class Node<E> {
  4. E item;
  5. Node<E> prev;
  6. Node<E> next;
  7. Node(E x, Node<E> p, Node<E> n) {
  8. item = x;
  9. prev = p;
  10. next = n;
  11. }
  12. }
  13. /** 头节点 */
  14. private transient Node<E> first;
  15. /** 尾节点 */
  16. private transient Node<E> last;
  17. /** 元素个数*/
  18. private transient int count;
  19. /** 队列容量 */
  20. private final int capacity;
  21. /** 锁 */
  22. private final ReentrantLock lock = new ReentrantLock();
  23. /** notEmpty条件 */
  24. private final Condition notEmpty = lock.newCondition();
  25. /** notFull条件 */
  26. private final Condition notFull = lock.newCondition();
  27. /** 构造方法 */
  28. public LinkedBlockingDeque() {
  29. this(Integer.MAX_VALUE);
  30. }
  31. public LinkedBlockingDeque(int capacity) {
  32. if (capacity <= 0) throw new IllegalArgumentException();
  33. this.capacity = capacity;
  34. }
  35. public LinkedBlockingDeque(Collection<? extends E> c) {
  36. this(Integer.MAX_VALUE);
  37. for (E e : c)
  38. add(e);
  39. }
  40. /**
  41. * 添加元素作为新的头节点
  42. */
  43. private boolean linkFirst(E e) {
  44. if (count >= capacity)
  45. return false;
  46. ++count;
  47. Node<E> f = first;
  48. Node<E> x = new Node<E>(e, null, f);
  49. first = x;
  50. if (last == null)
  51. last = x;
  52. else
  53. f.prev = x;
  54. notEmpty.signal();
  55. return true;
  56. }
  57. /**
  58. * 添加尾元素
  59. */
  60. private boolean linkLast(E e) {
  61. if (count >= capacity)
  62. return false;
  63. ++count;
  64. Node<E> l = last;
  65. Node<E> x = new Node<E>(e, l, null);
  66. last = x;
  67. if (first == null)
  68. first = x;
  69. else
  70. l.next = x;
  71. notEmpty.signal();
  72. return true;
  73. }
  74. /**
  75. * 返回并移除头节点
  76. */
  77. private E unlinkFirst() {
  78. Node<E> f = first;
  79. if (f == null)
  80. return null;
  81. Node<E> n = f.next;
  82. first = n;
  83. if (n == null)
  84. last = null;
  85. else
  86. n.prev = null;
  87. --count;
  88. notFull.signal();
  89. return f.item;
  90. }
  91. /**
  92. * 返回并移除尾节点
  93. */
  94. private E unlinkLast() {
  95. Node<E> l = last;
  96. if (l == null)
  97. return null;
  98. Node<E> p = l.prev;
  99. last = p;
  100. if (p == null)
  101. first = null;
  102. else
  103. p.next = null;
  104. --count;
  105. notFull.signal();
  106. return l.item;
  107. }
  108. /**
  109. * 移除节点x
  110. */
  111. private void unlink(Node<E> x) {
  112. Node<E> p = x.prev;
  113. Node<E> n = x.next;
  114. if (p == null) {//x是头的情况
  115. if (n == null)
  116. first = last = null;
  117. else {
  118. n.prev = null;
  119. first = n;
  120. }
  121. } else if (n == null) {//x是尾的情况
  122. p.next = null;
  123. last = p;
  124. } else {//x是中间的情况
  125. p.next = n;
  126. n.prev = p;
  127. }
  128. --count;
  129. notFull.signalAll();
  130. }
  131. //--------------------------------- BlockingDeque 双端阻塞队列方法实现
  132. public void addFirst(E e) {
  133. if (!offerFirst(e))
  134. throw new IllegalStateException("Deque full");
  135. }
  136. public void addLast(E e) {
  137. if (!offerLast(e))
  138. throw new IllegalStateException("Deque full");
  139. }
  140. public boolean offerFirst(E e) {
  141. if (e == null) throw new NullPointerException();
  142. lock.lock();
  143. try {
  144. return linkFirst(e);
  145. } finally {
  146. lock.unlock();
  147. }
  148. }
  149. public boolean offerLast(E e) {
  150. if (e == null) throw new NullPointerException();
  151. lock.lock();
  152. try {
  153. return linkLast(e);
  154. } finally {
  155. lock.unlock();
  156. }
  157. }
  158. public void putFirst(E e) throws InterruptedException {
  159. if (e == null) throw new NullPointerException();
  160. lock.lock();
  161. try {
  162. while (!linkFirst(e))
  163. notFull.await();
  164. } finally {
  165. lock.unlock();
  166. }
  167. }
  168. public void putLast(E e) throws InterruptedException {
  169. if (e == null) throw new NullPointerException();
  170. lock.lock();
  171. try {
  172. while (!linkLast(e))
  173. notFull.await();
  174. } finally {
  175. lock.unlock();
  176. }
  177. }
  178. public boolean offerFirst(E e, long timeout, TimeUnit unit)
  179. throws InterruptedException {
  180. if (e == null) throw new NullPointerException();
  181. long nanos = unit.toNanos(timeout);
  182. lock.lockInterruptibly();
  183. try {
  184. for (;;) {
  185. if (linkFirst(e))
  186. return true;
  187. if (nanos <= 0)
  188. return false;
  189. nanos = notFull.awaitNanos(nanos);
  190. }
  191. } finally {
  192. lock.unlock();
  193. }
  194. }
  195. public boolean offerLast(E e, long timeout, TimeUnit unit)
  196. throws InterruptedException {
  197. if (e == null) throw new NullPointerException();
  198. long nanos = unit.toNanos(timeout);
  199. lock.lockInterruptibly();
  200. try {
  201. for (;;) {
  202. if (linkLast(e))
  203. return true;
  204. if (nanos <= 0)
  205. return false;
  206. nanos = notFull.awaitNanos(nanos);
  207. }
  208. } finally {
  209. lock.unlock();
  210. }
  211. }
  212. public E removeFirst() {
  213. E x = pollFirst();
  214. if (x == null) throw new NoSuchElementException();
  215. return x;
  216. }
  217. public E removeLast() {
  218. E x = pollLast();
  219. if (x == null) throw new NoSuchElementException();
  220. return x;
  221. }
  222. public E pollFirst() {
  223. lock.lock();
  224. try {
  225. return unlinkFirst();
  226. } finally {
  227. lock.unlock();
  228. }
  229. }
  230. public E pollLast() {
  231. lock.lock();
  232. try {
  233. return unlinkLast();
  234. } finally {
  235. lock.unlock();
  236. }
  237. }
  238. public E takeFirst() throws InterruptedException {
  239. lock.lock();
  240. try {
  241. E x;
  242. while ( (x = unlinkFirst()) == null)
  243. notEmpty.await();
  244. return x;
  245. } finally {
  246. lock.unlock();
  247. }
  248. }
  249. public E takeLast() throws InterruptedException {
  250. lock.lock();
  251. try {
  252. E x;
  253. while ( (x = unlinkLast()) == null)
  254. notEmpty.await();
  255. return x;
  256. } finally {
  257. lock.unlock();
  258. }
  259. }
  260. public E pollFirst(long timeout, TimeUnit unit)
  261. throws InterruptedException {
  262. long nanos = unit.toNanos(timeout);
  263. lock.lockInterruptibly();
  264. try {
  265. for (;;) {
  266. E x = unlinkFirst();
  267. if (x != null)
  268. return x;
  269. if (nanos <= 0)
  270. return null;
  271. nanos = notEmpty.awaitNanos(nanos);
  272. }
  273. } finally {
  274. lock.unlock();
  275. }
  276. }
  277. public E pollLast(long timeout, TimeUnit unit)
  278. throws InterruptedException {
  279. long nanos = unit.toNanos(timeout);
  280. lock.lockInterruptibly();
  281. try {
  282. for (;;) {
  283. E x = unlinkLast();
  284. if (x != null)
  285. return x;
  286. if (nanos <= 0)
  287. return null;
  288. nanos = notEmpty.awaitNanos(nanos);
  289. }
  290. } finally {
  291. lock.unlock();
  292. }
  293. }
  294. public E getFirst() {
  295. E x = peekFirst();
  296. if (x == null) throw new NoSuchElementException();
  297. return x;
  298. }
  299. public E getLast() {
  300. E x = peekLast();
  301. if (x == null) throw new NoSuchElementException();
  302. return x;
  303. }
  304. public E peekFirst() {
  305. lock.lock();
  306. try {
  307. return (first == null) ? null : first.item;
  308. } finally {
  309. lock.unlock();
  310. }
  311. }
  312. public E peekLast() {
  313. lock.lock();
  314. try {
  315. return (last == null) ? null : last.item;
  316. } finally {
  317. lock.unlock();
  318. }
  319. }
  320. public boolean removeFirstOccurrence(Object o) {
  321. if (o == null) return false;
  322. lock.lock();
  323. try {
  324. for (Node<E> p = first; p != null; p = p.next) {
  325. if (o.equals(p.item)) {
  326. unlink(p);
  327. return true;
  328. }
  329. }
  330. return false;
  331. } finally {
  332. lock.unlock();
  333. }
  334. }
  335. public boolean removeLastOccurrence(Object o) {
  336. if (o == null) return false;
  337. lock.lock();
  338. try {
  339. for (Node<E> p = last; p != null; p = p.prev) {
  340. if (o.equals(p.item)) {
  341. unlink(p);
  342. return true;
  343. }
  344. }
  345. return false;
  346. } finally {
  347. lock.unlock();
  348. }
  349. }
  350. //---------------------------------- BlockingQueue阻塞队列 方法实现
  351. public boolean add(E e) {
  352. addLast(e);
  353. return true;
  354. }
  355. public boolean offer(E e) {
  356. return offerLast(e);
  357. }
  358. public void put(E e) throws InterruptedException {
  359. putLast(e);
  360. }
  361. public boolean offer(E e, long timeout, TimeUnit unit)
  362. throws InterruptedException {
  363. return offerLast(e, timeout, unit);
  364. }
  365. public E remove() {
  366. return removeFirst();
  367. }
  368. public E poll() {
  369. return pollFirst();
  370. }
  371. public E take() throws InterruptedException {
  372. return takeFirst();
  373. }
  374. public E poll(long timeout, TimeUnit unit) throws InterruptedException {
  375. return pollFirst(timeout, unit);
  376. }
  377. public E element() {
  378. return getFirst();
  379. }
  380. public E peek() {
  381. return peekFirst();
  382. }
  383. //------------------------------------------- Stack 方法实现
  384. public void push(E e) {
  385. addFirst(e);
  386. }
  387. public E pop() {
  388. return removeFirst();
  389. }
  390. //------------------------------------------- Collection 方法实现
  391. public boolean remove(Object o) {
  392. return removeFirstOccurrence(o);
  393. }
  394. public int size() {
  395. lock.lock();
  396. try {
  397. return count;
  398. } finally {
  399. lock.unlock();
  400. }
  401. }
  402. public boolean contains(Object o) {
  403. if (o == null) return false;
  404. lock.lock();
  405. try {
  406. for (Node<E> p = first; p != null; p = p.next)
  407. if (o.equals(p.item))
  408. return true;
  409. return false;
  410. } finally {
  411. lock.unlock();
  412. }
  413. }
  414. boolean removeNode(Node<E> e) {
  415. lock.lock();
  416. try {
  417. for (Node<E> p = first; p != null; p = p.next) {
  418. if (p == e) {
  419. unlink(p);
  420. return true;
  421. }
  422. }
  423. return false;
  424. } finally {
  425. lock.unlock();
  426. }
  427. }
  428. ……
  429. }

3、LinkedBlockingDeque的优缺点

有了上面的结论再来研究LinkedBlockingDeque的优缺点。

优点当然是功能足够强大,同时由于采用一个独占锁,因此实现起来也比较简单。所有对队列的操作都加锁就可以完成。同时独占锁也能够很好的支持双向阻塞的特性。

凡事有利必有弊。缺点就是由于独占锁,所以不能同时进行两个操作,这样性能上就大打折扣。从性能的角度讲LinkedBlockingDeque要比LinkedBlockingQueue要低很多,比CocurrentLinkedQueue就低更多了,这在高并发情况下就比较明显了。

前面分析足够多的Queue实现后,LinkedBlockingDeque的原理和实现就不值得一提了,无非是在独占锁下对一个链表的普通操作。

4、LinkedBlockingDeque的序列化、反序列化

有趣的是此类支持序列化,但是Node并不支持序列化,因此fist/last就不能序列化,那么如何完成序列化/反序列化过程呢?

清单4 LinkedBlockingDeque的序列化、反序列化

  1. <span style="font-size:14px;">private void writeObject(java.io.ObjectOutputStream s)
  2. throws java.io.IOException {
  3. lock.lock();
  4. try {
  5. // Write out capacity and any hidden stuff
  6. s.defaultWriteObject();
  7. // Write out all elements in the proper order.
  8. for (Node<E> p = first; p != null; p = p.next)
  9. s.writeObject(p.item);
  10. // Use trailing null as sentinel
  11. s.writeObject(null);
  12. } finally {
  13. lock.unlock();
  14. }
  15. }
  16. private void readObject(java.io.ObjectInputStream s)
  17. throws java.io.IOException, ClassNotFoundException {
  18. s.defaultReadObject();
  19. count = 0;
  20. first = null;
  21. last = null;
  22. // Read in all elements and place in queue
  23. for (;;) {
  24. E item = (E)s.readObject();
  25. if (item == null)
  26. break;
  27. add(item);
  28. }
  29. }
  30. </span>

清单4
描述的是LinkedBlockingDeque序列化/反序列化的过程。序列化时将真正的元素写入输出流,最后还写入了一个null。读取的时候将所有对象列表读出来,如果读取到一个null就表示结束。这就是为什么写入的时候写入一个null的原因,因为没有将count写入流,所以就靠null来表示结束,省一个整数空间。

参考内容来源:
集合框架 Queue篇(1)---ArrayDeque
http://hi.baidu.com/yao1111yao/item/1a1346f65a50d9c8521c266d
集合框架 Queue篇(7)---LinkedBlockingDeque
http://hi.baidu.com/yao1111yao/item/b1649cff2cf60be91a111f6d
深入浅出 Java Concurrency (24): 并发容器 part 9 双向队列集合 Deque
http://www.blogjava.net/xylz/archive/2010/08/12/328587.html
深入浅出 Java Concurrency (25): 并发容器 part 10 双向并发阻塞队列 BlockingDeque
http://www.blogjava.net/xylz/archive/2010/08/18/329227.html

Java多线程之Deque与LinkedBlockingDeque深入分析的更多相关文章

  1. Java多线程之ConcurrentSkipListMap深入分析(转)

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

  2. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  3. JAVA多线程之volatile 与 synchronized 的比较

    一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...

  4. java多线程之yield,join,wait,sleep的区别

    Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...

  5. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

  6. JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止

    JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...

  7. java多线程之wait和notify协作,生产者和消费者

    这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...

  8. Java——多线程之Lock锁

    Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...

  9. java多线程之:深入JVM锁机制2-Lock (转载)

    前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...

随机推荐

  1. 【手撸一个ORM】第九步、orm默认配置类 MyDbConfiguration,一次配置,简化实例化流程

    这个实现比较简单,事实上可配置的项目很多,如有需要,请读者自行扩展 using System; namespace MyOrm { public class MyDbConfiguration { p ...

  2. ASP.NET Core 2.0 源代码

    ASP.NET Core 2.0 源代码 在Visual Studio 2017中可以通过符号以及源链接,非常方便对 ASP.NET Core 2.0中源代码进行调试.在这篇文章中,我们将重点介绍如何 ...

  3. Identity Service

    Identity Service - 解析微软微服务架构eShopOnContainers(二)   接上一篇,众所周知一个网站的用户登录是非常重要,一站式的登录(SSO)也成了大家讨论的热点.微软在 ...

  4. linux替换文件中的某个字符串的命令sed

    sed -i 's/java-7-oracle/java-8-oracle/g' /etc/init.d/tomcat7 上面的命令是将tomcat7中的java-7-oracle替换为java-8- ...

  5. stm32的低功耗模式:

    一.待机模式.待机模式是低功耗中最低功耗的,内部电压调节电路被关闭,  HSE.HIS.PLL被关闭:进入待机模式后,SRAM和寄存器的内容将丢失.  (CPU停止,外设停止,RAM的数据寄存器的内容 ...

  6. 一道笔试题和UML思想 ~

    一句软件工程界的名言,让我想起了一个和一道笔试题有关的故事.希望更多的人了解 UML 背后的思想比他的语法更重要,是笔者写作本文的一点小愿望. 一.从一句软件工程名言说起 对很多事情的处理上,东西方都 ...

  7. 复习KMP

    KMP刚学的时候,看不懂. 再看,哇!原来是这样! 用的时候,忘了. 为了不再跌倒,我决定,记住吧... 在我看来,KMP一般用于字符串匹配时的防超时优化. 他的精髓就是,利用已经匹配的信息,简化这之 ...

  8. Unity3d中3D Text对模型的穿透显示

    昨晚,好友在电话里问我在Unity3d中使用3D Text,不想让其穿透模型显示,即想让场景中的3D Text与模型有正确的遮挡关系,怎么解? 今早谷歌上查了查,明白了原因,因为3D Text的默认材 ...

  9. AJPFX总结Java 类加载器

    顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中.一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java ...

  10. Gradle项目构建(1)——Gradle的由来

    一.项目自动构建介绍 作为Java的开发者对eclipse都非常熟悉,其实eclipse就是居于ant来构建项目的,我们先来看看为什么需要自动化构建项目. 1.为什么我们要自动化构建项目 可以假设我们 ...