LinkedBlockingQueue是BlockingQueue的一种使用Link List的实现,它对头和尾(取和添加操作)采用两把不同的锁,相对于ArrayBlockingQueue提高了吞吐量。它也是一种阻塞型的容器,适合于实现“消费者生产者”模式。

ArrayBlockingQueue是对BlockingQueue的一个数组实现,它使用一把全局的锁并行对queue的读写操作,同时使用两个Condition阻塞容量为空时的取操作和容量满时的写操作。

正因为LinkedBlockingQueue使用两个独立的锁控制数据同步,所以可以使存取两种操作并行执行,从而提高并发效率。而ArrayBlockingQueue使用一把锁,造成在存取两种操作争抢一把锁,而使得性能相对低下。LinkedBlockingQueue可以不设置队列容量,默认为Integer.MAX_VALUE.其容易造成内存溢出,一般要设置其值。

LinkedBlockingQueue底层的定义如下:

  1. public class LinkedBlockingQueue<E> extends AbstractQueue<E>
  2. implements BlockingQueue<E>, java.io.Serializable {
  3. static class Node<E> {
  4. /** The item, volatile to ensure barrier separating write and read */
  5. volatile E item;
  6. Node<E> next;
  7. Node(E x) { item = x; }
  8. }
  9. // 支持原子操作
  10. private final AtomicInteger count = new AtomicInteger(0);
  11. // 链表的头和尾
  12. private transient Node<E> head;
  13. private transient Node<E> last;
  14. // 针对取和添加操作的两把锁及其上的条件
  15. private final ReentrantLock takeLock = new ReentrantLock();
  16. private final Condition notEmpty = takeLock.newCondition();
  17. private final ReentrantLock putLock = new ReentrantLock();
  18. private final Condition notFull = putLock.newCondition();
  19. ...
  20. }

LinkedBlockingQueue的添加操作:

  1. public class LinkedBlockingQueue<E> extends AbstractQueue<E>
  2. implements BlockingQueue<E>, java.io.Serializable {
  3. private void insert(E x) {
  4. last = last.next = new Node<E>(x);
  5. }
  6. /**
  7. * signal方法在被调用时,当前线程必须拥有该condition相关的锁!
  8. * Signal a waiting take. Called only from put/offer (which do not
  9. * otherwise ordinarily lock takeLock.)
  10. */
  11. private void signalNotEmpty() {
  12. final ReentrantLock takeLock = this.takeLock;
  13. takeLock.lock();
  14. try {
  15. notEmpty.signal();
  16. } finally {
  17. takeLock.unlock();
  18. }
  19. }
  20. public void put(E o) throws InterruptedException {
  21. if (o == null) throw new NullPointerException();
  22. int c = -1;
  23. final ReentrantLock putLock = this.putLock;
  24. final AtomicInteger count = this.count;
  25. // 使用putLock
  26. putLock.lockInterruptibly();
  27. try {
  28. try {
  29. // 当容量已满时,等待notFull条件
  30. while (count.get() == capacity)
  31. notFull.await();
  32. } catch (InterruptedException ie) {
  33. notFull.signal(); // propagate to a non-interrupted thread
  34. throw ie;
  35. }
  36. insert(o);
  37. // 取出当前值,并将原数据增加1
  38. c = count.getAndIncrement();
  39. // 容量不满,再次激活notFull上等待的put线程
  40. if (c + 1 < capacity)
  41. notFull.signal();
  42. } finally {
  43. putLock.unlock();
  44. }
  45. // 必须先释放putLock再在notEmpty上signal,否则会造成死锁
  46. if (c == 0)
  47. signalNotEmpty();
  48. }
  49. ...
  50. }

LinkedBlockingQueue的取操作:

  1. public class LinkedBlockingQueue<E> extends AbstractQueue<E>
  2. implements BlockingQueue<E>, java.io.Serializable {
  3. private E extract() {
  4. Node<E> first = head.next;
  5. head = first;
  6. E x = first.item;
  7. first.item = null;
  8. return x;
  9. }
  10. private void signalNotFull() {
  11. final ReentrantLock putLock = this.putLock;
  12. putLock.lock();
  13. try {
  14. notFull.signal();
  15. } finally {
  16. putLock.unlock();
  17. }
  18. }
  19. public E take() throws InterruptedException {
  20. E x;
  21. int c = -1;
  22. final AtomicInteger count = this.count;
  23. final ReentrantLock takeLock = this.takeLock;
  24. // 使用takeLock
  25. takeLock.lockInterruptibly();
  26. try {
  27. try {
  28. // 若容量为空,等待notEmpty
  29. while (count.get() == 0)
  30. notEmpty.await();
  31. } catch (InterruptedException ie) {
  32. notEmpty.signal(); // propagate to a non-interrupted thread
  33. throw ie;
  34. }
  35. x = extract();
  36. c = count.getAndDecrement();
  37. // 再次激活notEmpty
  38. if (c > 1)
  39. notEmpty.signal();
  40. } finally {
  41. takeLock.unlock();
  42. }
  43. // take执行之前容量已满,则激活notFull
  44. if (c == capacity)
  45. signalNotFull();
  46. return x;
  47. }
  48. ...
  49. }

ArrayBlockingQueue底层定义如下:

  1. public class ArrayBlockingQueue<E> extends AbstractQueue<E>
  2. implements BlockingQueue<E>, java.io.Serializable {
  3. // 使用循环数组来实现queue,初始时takeIndex和putIndex均为0
  4. private final E[] items;
  5. private transient int takeIndex;
  6. private transient int putIndex;
  7. private int count;
  8. // 用于并发的锁和条件
  9. private final ReentrantLock lock;
  10. private final Condition notEmpty;
  11. private final Condition notFull;
  12. /**
  13. * 循环数组
  14. * Circularly increment i.
  15. */
  16. final int inc(int i) {
  17. return (++i == items.length)? 0 : i;
  18. }
  19. public ArrayBlockingQueue(int capacity, boolean fair) {
  20. if (capacity <= 0)
  21. throw new IllegalArgumentException();
  22. this.items = (E[]) new Object[capacity];
  23. // 分配锁及该锁上的condition
  24. lock = new ReentrantLock(fair);
  25. notEmpty = lock.newCondition();
  26. notFull =  lock.newCondition();
  27. }
  28. ...
  29. }

ArrayBlockingQueue的取操作:

  1. public class ArrayBlockingQueue<E> extends AbstractQueue<E>
  2. implements BlockingQueue<E>, java.io.Serializable {
  3. private E extract() {
  4. final E[] items = this.items;
  5. E x = items[takeIndex];
  6. items[takeIndex] = null;
  7. takeIndex = inc(takeIndex);
  8. --count;
  9. // 激发notFull条件
  10. notFull.signal();
  11. return x;
  12. }
  13. /**
  14. * condition的await的语义如下:
  15. * 与condition相关的锁以原子方式释放,并禁用该线程
  16. * 方法返回时,线程必须获得与该condition相关的锁
  17. */
  18. public E take() throws InterruptedException {
  19. final ReentrantLock lock = this.lock;
  20. lock.lockInterruptibly();
  21. try {
  22. try {
  23. // 等待notEmpty的条件
  24. while (count == 0)
  25. notEmpty.await();
  26. } catch (InterruptedException ie) {
  27. notEmpty.signal(); // propagate to non-interrupted thread
  28. throw ie;
  29. }
  30. E x = extract();
  31. return x;
  32. } finally {
  33. lock.unlock();
  34. }
  35. }
  36. ...
  37. }

ArrayBlockingQueue的写操作:

  1. public class ArrayBlockingQueue<E> extends AbstractQueue<E>
  2. implements BlockingQueue<E>, java.io.Serializable {
  3. private void insert(E x) {
  4. items[putIndex] = x;
  5. putIndex = inc(putIndex);
  6. ++count;
  7. notEmpty.signal();
  8. }
  9. public void put(E o) throws InterruptedException {
  10. if (o == null) throw new NullPointerException();
  11. final E[] items = this.items;
  12. final ReentrantLock lock = this.lock;
  13. lock.lockInterruptibly();
  14. try {
  15. try {
  16. // 等待notFull条件
  17. while (count == items.length)
  18. notFull.await();
  19. } catch (InterruptedException ie) {
  20. notFull.signal(); // propagate to non-interrupted thread
  21. throw ie;
  22. }
  23. insert(o);
  24. } finally {
  25. lock.unlock();
  26. }
  27. }
  28. ...
  29. }

注意:ArrayBlockingQueue在读写操作上都需要锁住整个容器,因此吞吐量与一般的实现是相似的,适合于实现“生产者消费者”模式。

通过保证在临界区上多个线程的相互排斥,线程间可以完全避免竞争状态的发生,但是有时候还是需要线程之间的相互协作。使用条件(Condition)便于线程间通信。一个线程可以指定在某种条件下该做什么。标间是通过调用Lock对象的newCoditionn()方法来实现线程之间的相互通信的。

一旦创建一个条件,就可使用await()、signal()、signalAll()方法来实现线程间通信。await()方法可以让当前线程都处于等待状态,知道条件放生。signal()方法唤醒一个等待的线程,而signalAll()方法唤醒所有等待线程。

注:

Lock接口的 线程请求锁的 几个方法:

lock(), 拿不到lock就不罢休,不然线程就一直block。 比较无赖的做法。
tryLock(),马上返回,拿到lock就返回true,不然返回false。 比较潇洒的做法。
带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false。比较聪明的做法。

下面的lockInterruptibly()就稍微难理解一些。

先说说线程的打扰机制,每个线程都有一个 打扰 标志。这里分两种情况,
1. 线程在sleep或wait,join, 此时如果别的进程调用此进程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException;(thread在做IO操作时也可能有类似行为,见java thread api)
2. 此线程在运行中, 则不会收到提醒。但是 此线程的 “打扰标志”会被设置, 可以通过isInterrupted()查看并 作出处理。

lockInterruptibly()和上面的第一种情况是一样的, 线程在请求lock并被阻塞时,如果被interrupt,则“此线程会被唤醒并被要求处理InterruptedException”。

引自:http://zhuhui-zj.iteye.com/blog/784193

BlockQueue中ArrayBlockingQueue和LinkedBlockingQueue比较的更多相关文章

  1. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

  2. ArrayBlockingQueue跟LinkedBlockingQueue的区别

    .队列中的锁的实现不同 ArrayBlockingQueue中的锁是没有分离的,即生产和消费用的是同一个锁: LinkedBlockingQueue中的锁是分离的,即生产用的是putLock,消费是t ...

  3. ArrayBlockingQueue和LinkedBlockingQueue分析

        JAVA并发包提供三个常用的并发队列实现,分别是:ConcurrentLinkedQueue.LinkedBlockingQueue和ArrayBlockingQueue. Concurren ...

  4. ArrayBlockingQueue和LinkedBlockingQueue的区别

    ArrayBlockingQueue和LinkedBlockingQueue的区别,得出结论如下: 1. 队列中锁的实现不同 ArrayBlockingQueue实现的队列中的锁是没有分离的,即生产和 ...

  5. Java 容器源码分析之ArrayBlockingQueue和LinkedBlockingQueue

    Java中的阻塞队列接口BlockingQueue继承自Queue接口. BlockingQueue接口提供了3个添加元素方法. add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会 ...

  6. ArrayBlockingQueue和LinkedBlockingQueue

    1.BlockingQueue接口定义了一种阻塞的FIFO queue ArrayBlockingQueue和LinkedBlockingQueue的区别: 1. 队列中锁的实现不同 ArrayBlo ...

  7. ArrayBlockingQueue,LinkedBlockingQueue分析

    BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会 ...

  8. 20.并发容器之ArrayBlockingQueue和LinkedBlockingQueue实现原理详解

    1. ArrayBlockingQueue简介 在多线程编程过程中,为了业务解耦和架构设计,经常会使用并发容器用于存储多线程间的共享数据,这样不仅可以保证线程安全,还可以简化各个线程操作.例如在“生产 ...

  9. java并发编程(二十四)----(JUC集合)ArrayBlockingQueue和LinkedBlockingQueue介绍

    这一节我们来了解阻塞队列(BlockingQueue),BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,当容量满时往BlockingQ ...

随机推荐

  1. esp和ebp指针

    gdb调试的时候会出现esp和ebp这两个指针,而这两个指针为我们查看栈的情况提供了方便. 简单点说,esp指向栈顶,而ebp指向栈底.例如一段程序: #include <stdio.h> ...

  2. [Swift通天遁地]二、表格表单-(15)自定义表单文本框内容的格式

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  3. 我的周记1——”云想衣裳花想容"

    这里记录过去一周,我学习到的,思考的,看到的,每周五发布. http 网上参考http入门协议  https://juejin.im/post/5afad7f16fb9a07abf72ac30 超文本 ...

  4. Scaffold-DbContext-EFCore DB First

    在使用 Scaffold-DbContext迁移数据库的时候会遇到两个问题. 一.文件已存在,其实报错很明显,增加 -force即可. 二.大小写转换,不和数据库一样了,如果要保持和数据库一致.增加  ...

  5. 用Python一键搭建Http服务器的方法

    用Python一键搭建Http服务器的方法 Python3请看 python -m http.server 8000 & Python2请看 python -m SimpleHTTPServe ...

  6. 【BZOJ2762】[JLOI2011]不等式组(树状数组)

    题目: BZOJ2762 分析: 加入的不等式分三种情况 当\(a>0\),可以变成\(x>\lfloor \frac{c-b}{a}\rfloor\) 当\(a=0\),若\(b> ...

  7. Android ScrollView里嵌套RecyclerView时,在RecyclerView上滑动时出现卡顿(冲突)的现象

    最近在项目中遇到一个现象,一个界面有一个RecyclerView(GridView型的),外面套了一层ScrollView,通过ScrollView上下滚动,但是在滑动的时候如果是在RecyclerV ...

  8. 完整版本的停车场管理系统源代码带服务端+手机android客户端

    该源码是停车场管理软件附带源代码 J2EE服务端+android客户端,也是一套停车场管理车辆进出的管理软,喜欢的朋友可以看看吧. 应用的后台管理主要功能介绍:1  机构管理 ,机构有从属管理< ...

  9. Leetcode0457--Circular Array Loop

    [转载请注明]https://www.cnblogs.com/igoslly/p/9339478.html class Solution { public: bool circularArrayLoo ...

  10. JAVA和JVM运行原理揭秘

    这里和大家简单分享一下JAVA和JVM运行的原理,Java语言写的源程序通过Java编译器,编译成与平台无关的‘字节码程序’(.class文件,也就是0,1二进制程序),然后在OS之上的Java解释器 ...