有大小的队列就叫有界队列 如 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. MS SqlServer之Exec和EXEC SP_EXECUTESQL

    exec执行sql时字符串时,不能给变量赋值,如果要在sql里给变量赋值,请用EXEC SP_EXECUTESQL 示例: 通过 SP_EXECUTESQL 的第2个参数来定义有哪些参数 输出的加OU ...

  2. JAVA 员工管理系统(用抽象类实现),简易版。

    package Demo513; /* 定义一个Employee类,该类包含: private 成员变量name,number,birthday,其中birthday为MyDate类的对象: abst ...

  3. webstorm增加内存配置参数

    webstorm增加内存配置参数 找到WebStorm.exe.vmoptions这个文件,路径如下 webstorm安装主目录>bin>WebStorm.exe.vmoptions 更改 ...

  4. vue-cli3项目中解决动态引入图片img404的问题

    博主最近手头再做一个项目,需要调用天气接口,并且还要动态显示天气相关图片icon. 本来以为没什么大问题,结果硬生生被这个动态图片路径搞得民不聊生(博主还是 too young,too simple~ ...

  5. Failed to crunch file

    Failed to crunch file 编译时,出现以上错误,经过多次排除验证,原因尽然是因为路径字符太长了... 编译路径不能超过240个字符

  6. JAVA程序员必须要学习的知识

    Java是热门的语言之一,TIOBE编程语排名Java排名第二,仅在C语言之后.Java可以用来开发web应用和桌面应用,更重要的是Java具有跨平台性:write once, run everywh ...

  7. CF 55D Beautiful numbers (数位DP)

    题意: 如果一个正整数能被其所有位上的数字整除,则称其为Beautiful number,问区间[L,R]共有多少个Beautiful number?(1<=L<=R<=9*1018 ...

  8. Linux OpenGL 实践篇-16 文本绘制

    文本绘制 本文主要射击Freetype的入门理解和在OpenGL中实现文字的渲染. freetype freetype的官网,本文大部分内容参考https://www.freetype.org/fre ...

  9. [VC]获取本地程序的版本信息信息

    CString CQwerApp::IS_GetAppVersion(char *AppName) { ////需要加上version.lib在link里 CString AppVersion; // ...

  10. UVA 818 Cutting Chains 切断圆环链 (暴力dfs)

    题意就是给一张无向图,去掉某些结点,然后连成一条链,问最少去掉几个结点. n很小n<=15,所以直接枚举2^15个状态就行啦. 链的条件是1.无环,2.没有度大于2的点,3.把n个散链连起来需要 ...