功能简介:
  • LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列。队列中的元素遵循先入先出 (FIFO)的规则。新元素插入到队列的尾部,从队列头部取出元素。(在并发程序中,基于链表实现的队列和基于数组实现的队列相比,往往具有更高的吞吐 量,但性能稍差一些)
源码分析:
  • 首先看下LinkedBlockingQueue内部的数据结构:
  1. public class LinkedBlockingQueue<E> extends AbstractQueue<E>
  2. implements BlockingQueue<E>, java.io.Serializable {
  3. private static final long serialVersionUID = -6903933977591709194L;
  4.  
  5. /**
  6. * Linked list node class
  7. */
  8. static class Node<E> {
  9. /** The item, volatile to ensure barrier separating write and read */
  10. volatile E item;
  11. Node<E> next;
  12. Node(E x) { item = x; }
  13. }
  14. /** The capacity bound, or Integer.MAX_VALUE if none */
  15. private final int capacity;
  16. /** 这里的count为原子量,避免了一些使用count的地方需要加两把锁。 */
  17. private final AtomicInteger count = new AtomicInteger(0);
  18. /** Head of linked list */
  19. private transient Node<E> head;
  20. /** Tail of linked list */
  21. private transient Node<E> last;
  22. /** Lock held by take, poll, etc */
  23. private final ReentrantLock takeLock = new ReentrantLock();
  24. /** Wait queue for waiting takes */
  25. private final Condition notEmpty = takeLock.newCondition();
  26. /** Lock held by put, offer, etc */
  27. private final ReentrantLock putLock = new ReentrantLock();
  28. /** Wait queue for waiting puts */
  29. private final Condition notFull = putLock.newCondition();
  30.  
  31. /**
  32. * Creates a <tt>LinkedBlockingQueue</tt> with a capacity of
  33. * {@link Integer#MAX_VALUE}.
  34. */
  35. public LinkedBlockingQueue() {
  36. this(Integer.MAX_VALUE);
  37. }
  38. /**
  39. * Creates a <tt>LinkedBlockingQueue</tt> with the given (fixed) capacity.
  40. *
  41. * @param capacity the capacity of this queue
  42. * @throws IllegalArgumentException if <tt>capacity</tt> is not greater
  43. * than zero
  44. */
  45. public LinkedBlockingQueue(int capacity) {
  46. if (capacity <= 0) throw new IllegalArgumentException();
  47. this.capacity = capacity;
  48. last = head = new Node<E>(null);
  49. }
  50.  
  51. public LinkedBlockingQueue(Collection<? extends E> c) {
  52. this(Integer.MAX_VALUE);
  53. for (E e : c)
  54. add(e);
  55. }

  首先可见,内部为单向链表;其次,内部为两把锁:存锁和取锁,并分别关联一个条件(是一种双锁队列)。

  • 还是从put和take入手,先看下put方法:
  1. public void put(E e) throws InterruptedException {
  2. if (e == null) throw new NullPointerException();
  3. // Note: convention in all put/take/etc is to preset
  4. // local var holding count negative to indicate failure unless set.
  5. int c = -1;
  6. final ReentrantLock putLock = this.putLock;
  7. final AtomicInteger count = this.count;
  8. putLock.lockInterruptibly();
  9. try {
  10. /*
  11. * Note that count is used in wait guard even though it is
  12. * not protected by lock. This works because count can
  13. * only decrease at this point (all other puts are shut
  14. * out by lock), and we (or some other waiting put) are
  15. * signalled if it ever changes from
  16. * capacity. Similarly for all other uses of count in
  17. * other wait guards.
  18. */
  19. try {
  20. while (count.get() == capacity)
  21. notFull.await();
  22. } catch (InterruptedException ie) {
  23. notFull.signal(); // propagate to a non-interrupted thread
  24. throw ie;
  25. }
  26. insert(e);
  27. c = count.getAndIncrement();
  28. if (c + 1 < capacity)
  29. /*
  30. * 注意这里的处理:和单锁队列不同,count为原子量,不需要锁保护。
  31. * put过程中可能有其他线程执行多次get,所以这里需要判断一下当前
  32. * 如果还有剩余容量,那么继续唤醒notFull条件上等待的线程。
  33. */
  34. notFull.signal();
  35. } finally {
  36. putLock.unlock();
  37. }
  38. if (c == 0) //如果count又0变为1,说明在队列是空的情况下插入了1个元素,唤醒notNull条件上等待的线程。
  39. signalNotEmpty();
  40. }
  41. /**
  42. * Creates a node and links it at end of queue.
  43. * @param x the item
  44. */
  45. private void insert(E x) {
  46. last = last.next = new Node<E>(x);
  47. }
  48. /**
  49. * Signals a waiting take. Called only from put/offer (which do not
  50. * otherwise ordinarily lock takeLock.)
  51. */
  52. private void signalNotEmpty() {
  53. final ReentrantLock takeLock = this.takeLock;
  54. takeLock.lock();
  55. try {
  56. notEmpty.signal();
  57. } finally {
  58. takeLock.unlock();
  59. }
  60. }

代码很容易看懂,再看下take方法实现:

  1. public E take() throws InterruptedException {
  2. E x;
  3. int c = -1;
  4. final AtomicInteger count = this.count;
  5. final ReentrantLock takeLock = this.takeLock;
  6. takeLock.lockInterruptibly();
  7. try {
  8. try {
  9. while (count.get() == 0)
  10. notEmpty.await();
  11. } catch (InterruptedException ie) {
  12. notEmpty.signal(); // propagate to a non-interrupted thread
  13. throw ie;
  14. }
  15. x = extract();
  16. c = count.getAndDecrement();
  17. if (c > 1)
  18. notEmpty.signal();
  19. } finally {
  20. takeLock.unlock();
  21. }
  22. if (c == capacity)
  23. signalNotFull();
  24. return x;
  25. }
  26. /**
  27. * Removes a node from head of queue,
  28. * @return the node
  29. */
  30. private E extract() {
  31. Node<E> first = head.next;
  32. head = first;
  33. E x = first.item;
  34. first.item = null;
  35. return x;
  36. }
  37. /**
  38. * Signals a waiting put. Called only from take/poll.
  39. */
  40. private void signalNotFull() {
  41. final ReentrantLock putLock = this.putLock;
  42. putLock.lock();
  43. try {
  44. notFull.signal();
  45. } finally {
  46. putLock.unlock();
  47. }
  48. }

和put对等的逻辑,也很容易看懂。

  • 上面看到,主要方法里并没有同时用两把锁,但有些方法里会同时使用两把锁,比如remove方法等:
  1. public boolean remove(Object o) {
  2. if (o == null) return false;
  3. boolean removed = false;
  4. fullyLock();
  5. try {
  6. Node<E> trail = head;
  7. Node<E> p = head.next;
  8. while (p != null) {
  9. if (o.equals(p.item)) {
  10. removed = true;
  11. break;
  12. }
  13. trail = p;
  14. p = p.next;
  15. }
  16. if (removed) {
  17. p.item = null;
  18. trail.next = p.next;
  19. if (last == p)
  20. last = trail;
  21. if (count.getAndDecrement() == capacity)
  22. notFull.signalAll();
  23. }
  24. } finally {
  25. fullyUnlock();
  26. }
  27. return removed;
  28. }
  29. /**
  30. * Lock to prevent both puts and takes.
  31. */
  32. private void fullyLock() {
  33. putLock.lock();
  34. takeLock.lock();
  35. }
  36. /**
  37. * Unlock to allow both puts and takes.
  38. */
  39. private void fullyUnlock() {
  40. takeLock.unlock();
  41. putLock.unlock();
  42. }

Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue的更多相关文章

  1. Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue

    功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...

  2. 【JUC源码解析】LinkedBlockingQueue

    简介 一个基于链表的阻塞队列,FIFO的顺序,head指向的元素等待时间最长,tail指向的元素等待时间最短,新元素从队列尾部添加,检索元素从队列头部开始,队列的容量,默认是Integer#MAX_V ...

  3. Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer

    功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...

  4. Jdk1.6 JUC源码解析(7)-locks-ReentrantLock

    功能简介: Java代码层面提供的锁机制,可做为Synchronized(jvm内置)的替代物,和Synchronized一样都是可重入的. 与Synchronized相比较而言,ReentrantL ...

  5. Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX

    转自:http://brokendreams.iteye.com/blog/2250109 功能简介: 原子量和普通变量相比,主要体现在读写的线程安全上.对原子量的是原子的(比如多线程下的共享变量i+ ...

  6. 【JUC源码解析】ScheduledThreadPoolExecutor

    简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...

  7. 【JUC源码解析】ForkJoinPool

    简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...

  8. 【JUC源码解析】Exchanger

    简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...

  9. 【JUC源码解析】SynchronousQueue

    简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...

随机推荐

  1. crontab的定时任务不能自动执行,但是手动执行脚本一直能成功

    crontab 问题小记: 环境变量问题, 养成良好的习惯, 在脚本开头export PATH 原因是 crontab 执行定时任务时,用的不是系统环境变量,而是自己的环境变量,可以把 echo $P ...

  2. Alamofire源码解读系列(十一)之多表单(MultipartFormData)

    本篇讲解跟上传数据相关的多表单 前言 我相信应该有不少的开发者不明白多表单是怎么一回事,然而事实上,多表单确实很简单.试想一下,如果有多个不同类型的文件(png/txt/mp3/pdf等等)需要上传给 ...

  3. 关于oracle后导数据的一些小TIPS

    今天下午需要把一些数据导入到正式环境中,但是通过Excel拷贝进去行会错位,把excel的每一列的双击让其变为最宽即可解决该问题

  4. new表达式如何创建对象

    new表达式如何创建对象 前言 刚学java时曾一度认为,构造器中this指向是当前类型的对象,当我们调用new表达式时,由父类的构造器生成对象的一部分并初始化,然后再由子类的构造器补充成完整的对象并 ...

  5. [SinGuLaRiTy] 树形DP专项测试

    [SinGuLaRiTy-1015] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 对于所有的题目:Time Limit:1s  |  Me ...

  6. 网站防止SQL注入方法

    方法:所有获取GET.POST变量都先进行过滤: 字符串-- htmlspecialchars(addslashes($string))  addslashes() 函数返回在预定义字符之前添加反斜杠 ...

  7. 《JavaScript程序设计》第2课:JS类型系统

    JS类型系统可以分为标准类型和对象类型,进一步标准类型又可以分为原始类型和引用类型,而对象类型又可以分为内置对象类型.普通对象类型.自定义对象类型. 1. 标准类型 标准类型共包括了6个分别是:und ...

  8. python 面向对象编程(二)

    在上一篇文章中谈到了类的基本定义和使用方法,这只体现了面向对象编程的三大特点之一:封装. 下面就来了解一下另外两大特征:继承和多态. 在Python中,如果需要的话,可以让一个类去继承一个类,被继承的 ...

  9. 你说你精通CSS,真的吗?

    以前做项目的时候,学习了HTML和CSS,感觉这两个比较简单,在W3school里学习了一下之后,就觉得自己已经没问题了.可是,真正要做一个好看的页面,我还是要写好久.其实,对于CSS,我并没有像我以 ...

  10. 图解函数重载以及arguments