上一篇我们说了并发队列中的LinkedBlockingQueue队列,这次我们看看ArrayBlockingQueue,看看名字,我们想象一下LinkedList和ArrayList的区别,我们可以知道ArrayBlockingQueue底层肯定是基于数组实现的,这是一个有界数组;

  ArrayBlockingQueue其中的组成部分和LinkedBlockingQueue及其相似,也是有两个条件变量,维护阻塞队列,实现了生产消费者模式;

一.简单认识ArrayBlockingQueue

  先看看几个常用属性:

  1. //数组用于存放队列元素
  2. final Object[] items;
  3. //出队索引
  4. int takeIndex;
  5. //入队索引
  6. int putIndex;
  7. //队列中元素数量
  8. int count;
  9. //独占锁
  10. final ReentrantLock lock;
  11. //如果数组中为空,还有线程取数据,就丢到这个条件变量中来阻塞
  12. private final Condition notEmpty;
  13. //队列满了,还有线程往数组中添加数据,就把线程丢到这里来阻塞
  14. private final Condition notFull;

  由于这是一个有界的数组,我们再看看构造器:

  1. //指定容量,默认是非公平策略
  2. public ArrayBlockingQueue(int capacity) {
  3. this(capacity, false);
  4. }
  5. //指定容量和独占锁的策略
  6. public ArrayBlockingQueue(int capacity, boolean fair) {
  7. if (capacity <= 0)
  8. throw new IllegalArgumentException();
  9. this.items = new Object[capacity];
  10. lock = new ReentrantLock(fair);
  11. notEmpty = lock.newCondition();
  12. notFull = lock.newCondition();
  13. }
  14. //可以指定容量,锁的策略,还有初始化数据
  15. public ArrayBlockingQueue(int capacity, boolean fair,
  16. Collection<? extends E> c) {
  17. this(capacity, fair);
  18.  
  19. final ReentrantLock lock = this.lock;
  20. lock.lock(); // Lock only for visibility, not mutual exclusion
  21. try {
  22. int i = 0;
  23. try {
  24. for (E e : c) {
  25. checkNotNull(e);
  26. items[i++] = e;
  27. }
  28. } catch (ArrayIndexOutOfBoundsException ex) {
  29. throw new IllegalArgumentException();
  30. }
  31. count = i;
  32. putIndex = (i == capacity) ? 0 : i;
  33. } finally {
  34. lock.unlock();
  35. }
  36. }

二.offer方法

  向队列尾部添加一个元素,添加成功就返回true,队列满了就丢掉当前元素直接返回false,方法不阻塞;

  1. public boolean offer(E e) {
  2. //非空检验
  3. checkNotNull(e);
  4. final ReentrantLock lock = this.lock;
  5. lock.lock();
  6. try {
  7. //如果数组中实际数量和最大容量相等,添加失败,返回false
  8. if (count == items.length)
  9. return false;
  10. else {
  11. //添加成功,方法实现在下面
  12. enqueue(e);
  13. return true;
  14. }
  15. } finally {
  16. //释放锁
  17. lock.unlock();
  18. }
  19. }
  20. private void enqueue(E x) {
  21. //拿到数组
  22. final Object[] items = this.items;
  23. //在putIndex这个位置放入数据x,然后把putIndex加一,说明这个参数表示的是下一个数据要放入的位置的索引
  24. items[putIndex] = x;
  25. //这里putIndex是先加一然后再比较是否相等,比如这里数组的最大容量是5,那么索引的最大值应该是4,而如果putIndex等于5了,说明数组
  26. //越界了,加把这个索引重置为0
  27. if (++putIndex == items.length)
  28. putIndex = 0;
  29. count++;
  30. //添加完成之后,说明了数组中有数据了,这里会唤醒之前因为去数组中取数据而阻塞的线程
  31. notEmpty.signal();
  32. }

三.put方法

  向队列尾部插入一个元素,队列有空闲就插入成功返回true,队列满了就阻塞当前线程到notFull的条件队列中,等有空闲之后就会被唤醒;阻塞过程中对中断会有响应的;

  1. public void put(E e) throws InterruptedException {
  2. //非空检查
  3. checkNotNull(e);
  4. final ReentrantLock lock = this.lock;
  5. //注意该锁的获取方式
  6. lock.lockInterruptibly();
  7. try {
  8. //如果线程满了,就把当前线程放到notFull条件变量的阻塞队列中
  9. while (count == items.length)
  10. notFull.await();
  11. //没有满,就添加数据
  12. enqueue(e);
  13. } finally {
  14. //释放锁
  15. lock.unlock();
  16. }
  17. }

四.poll方法

  头部获取并移除一个元素,如果队列为空,就返回null,方法不阻塞;

  1. public E poll() {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock();
  4. try {
  5. //如果队列为空,就返回null
  6. //如果队列不为空,就调用dequeue方法获取并删除队列头部的元素
  7. return (count == 0) ? null : dequeue();
  8. } finally {
  9. lock.unlock();
  10. }
  11. }
  12. private E dequeue() {
  13. //获取数组
  14. final Object[] items = this.items;
  15. @SuppressWarnings("unchecked")
  16. //获取takeIndex位置的元素,最后会将这个返回
  17. E x = (E) items[takeIndex];
  18. //然后将takeInde位置置为空
  19. items[takeIndex] = null;
  20. //如果takeIndex已经是数组的最后一个位置了,就将takeIndex重置为0
  21. if (++takeIndex == items.length)
  22. takeIndex = 0;
  23. //实际数量减一
  24. count--;
  25. if (itrs != null)
  26. itrs.elementDequeued();
  27. //唤醒notFull中线程
  28. notFull.signal();
  29. return x;
  30. }

五.take方法

  获取并删除当前队列头部的元素,如果队列为空当前线程阻塞直到被唤醒,对中断有响应;

  1. public E take() throws InterruptedException {
  2. final ReentrantLock lock = this.lock;
  3. //可中断的方式获取锁
  4. lock.lockInterruptibly();
  5. try {
  6. //如果数组为空,此时就唤醒notEmpty中条件队列里的线程
  7. while (count == 0)
  8. notEmpty.await();
  9. //获取并删除头节点
  10. return dequeue();
  11. } finally {
  12. lock.unlock();
  13. }
  14. }

六.peek方法

  只是获取头部元素,不删除,如果队列为空就返回null,这个方法是线程不阻塞的

  1. public E peek() {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock();
  4. try {
  5. return itemAt(takeIndex); // null when queue is empty
  6. } finally {
  7. lock.unlock();
  8. }
  9. }
  10.  
  11. //获取到数组中索引为takeIndex中的数据
  12. @SuppressWarnings("unchecked")
  13. final E itemAt(int i) {
  14. return (E) items[i];
  15. }

七.总结

  理解了上一篇博客中说的LinkedBlockingQueue,那么再看这一篇其实太容易了,就是操作数组嘛!用下面这个图表示:

并发队列之ArrayBlockingQueue的更多相关文章

  1. 并发队列:ArrayBlockingQueue实际运用场景和原理

    ArrayBlockingQueue实际应用场景 之前在某公司做过一款情绪识别的系统,这套系统通过调用摄像头接口采集人脸信息,将采集的人脸信息做人脸识别和情绪分析,最终经过一定的算法将个人情绪数据转化 ...

  2. 10.并发包阻塞队列之ArrayBlockingQueue

    上一节中对并发包中的非阻塞队列ConcurrentLinkedQueue的入队.出队做了一个简要的分析,本文将对并发包中的阻塞队列做一个简要分析. Java并发包中的阻塞队列一共7个,当然他们都是线程 ...

  3. 并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析

    LinkedBlockingQueue 在看源码之前,通过查询API发现对LinkedBlockingQueue特点的简单介绍: 1.LinkedBlockingQueue是一个由链表实现的有界队列阻 ...

  4. 并发队列ConcurrentLinkedQueue、阻塞队列AraayBlockingQueue、阻塞队列LinkedBlockingQueue 区别和使用场景总结

      三者区别与联系: 联系,三者 都是线程安全的.区别,就是 并发  和 阻塞,前者为并发队列,因为采用cas算法,所以能够高并发的处理:后2者采用锁机制,所以是阻塞的.注意点就是前者由于采用cas算 ...

  5. 并发包阻塞队列之ArrayBlockingQueue

    并发包阻塞队列之ArrayBlockingQueue   jdk1.7.0_79  上一节中对并发包中的非阻塞队列ConcurrentLinkedQueue的入队.出队做了一个简要的分析,本文将对并发 ...

  6. JAVA多线程(二) 并发队列和阻塞队列

    github代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-service/ ...

  7. Java深入学习(2):并发队列

    并发队列: 在并发队列中,JDK有两套实现: ConcurrentLinkedQueue:非阻塞式队列 BlockingQueue:阻塞式队列 阻塞式队列非阻塞式队列的区别: 阻塞式队列入列操作的时候 ...

  8. 【Java并发】并发队列与线程池

    并发队列 阻塞队列与非阻塞队 ConcurrentLinkedQueue BlockingQueue ArrayBlockingQueue LinkedBlockingQueue PriorityBl ...

  9. 并发队列 ConcurrentLinkedQueue 及 BlockingQueue 接口实现的四种队列

    队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有元素时,称为空队列. 在队列这 ...

随机推荐

  1. Yii2 框架下 session跨域共享互通

    在项目实施过程中,往往把一个大项目进行分拆成几个独立的项目,项目用完全独立的域名和文件,可以放到不同的服务器上的独立分项目. 几个子项目共用一个登录点. 原理简单来说就是服务端session 共享, ...

  2. XSS 3

    打开第三题然后会看到 然后进行一下添加数据 然后会发现数据被添加到 value=""双引号中然后然后我们会想到提前闭合 代码 然后进行编码 然后就可以通过了 此题与xss 2类似 ...

  3. 【代码总结】Struts2 Action接受参数方式的对比

    一.属性方式 1.Action中:对应表单参数的setter.getter 2.页面中  :Form中元素name取值属性名 <s:property value="属性名" ...

  4. 将html代码部署到阿里云服务器,并进行域名解析,以及在部署过程中遇到的问题和解决方法

    本博客主要是说一下,,如何将html代码部署到阿里云服务器,并进行域名解析,以及在部署过程中遇到的问题和解决方法. 1.先在阿里云上购买一台阿里云服务器(ECS云服务器): 2.远程连接上该服务器,在 ...

  5. opencv安装中的各种问题汇总

    问题1:opencv-2.4.10/modules/gpu/src/nvidia/core/NCV.cu(356): error : namespace "std" has no ...

  6. 寒假安卓app开发学习记录(2)

    今天属实是头疼的一天.开始的时候是简单了解了一下安卓的系统架构,了解到大概分为四个部分. 然后看了两节创建安卓项目的课程,准备去实践一下的时候突然发现我的eclipse里竟然没有Android选项.查 ...

  7. 使用Newtonsoft序列化对象,实现深拷贝

    工作记录 深拷贝:全新创建一个对象,值与复制对象一致,两个对象互不相关,修改一个对象不会影响到另一个对象 浅拷贝:全新创建一个对象,值与复制对象一致,两个对象相关,修改一个对象影响到另一个对象 在工作 ...

  8. jmeter实现服务器端后台接口性能测试

    实现目的 在进行服务器端后台接口性能测试时,需要连接到Linux服务器端,然后通过命令调用socket接口,这个过程就需要用到jmeter的SSH Command取样器实现了. 脚本实现 设置CSV ...

  9. 117. 填充每个节点的下一个右侧节点指针 II

    Q: 给定一个二叉树 struct Node { int val; Node *left; Node *right; Node *next; } 填充它的每个 next 指针,让这个指针指向其下一个右 ...

  10. 吴裕雄 python 神经网络——TensorFlow实现AlexNet模型处理手写数字识别MNIST数据集

    import tensorflow as tf # 输入数据 from tensorflow.examples.tutorials.mnist import input_data mnist = in ...