Java源码解析阻塞队列ArrayBlockingQueue介绍

Java的阻塞队列,在实现时,使用到了lock和condition,下面是对其主要方法的介绍。

首先看一下,阻塞队列中使用到的锁。

/** Main lock guarding all access **/
  final ReentrantLock lock;​
  /** Condition for waiting takes **/
  private final Condition notEmpty;​
  /** Condition for waiting puts **/
  private final Condition notFull;

主要的锁是一个可重入锁,根据注释,它是用来保证所有访问的同步。此外,还有2个condition,notEmpty用于take等待,notFull用于put等待。

两个condition的初始化方法如下:

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
      throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull = lock.newCondition();
  }

下面介绍一下put方法。代码如下。

public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      while (count == items.length)
        notFull.await();
      enqueue(e);
    } finally {
      lock.unlock();
    }
  }

进行put时,首先对待插入的元素进行了非null判断。然后获取锁。之后用一个循环进行判断,如果元素已满,那么,就调用notFull的await方法,进行阻塞。当有别的线程(其实是take元素的线程)调用notFull的siginal方法后,put线程会被唤醒。唤醒后再确认一下count是否小于items.length,如果是,则进行加入队列的操作。

下面介绍一下take方法,代码如下:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      while (count == 0)
        notEmpty.await();
      return dequeue();
    } finally {
      lock.unlock();
    }
  }

进行take时,同样先要获取锁,然后判断元素个数是否为0,为0时需要等待在notEmpty条件上,等待被唤醒。唤醒之后,会再进行一次元素个数判断,然后进行出队列操作。

分析代码到这里的时候,我产生了一个疑问,如果当前队列慢了,执行put的线程在获取到锁之后,等待notFull条件上。那么,当执行take操作的线程想获取锁时,阻塞队列的锁已经被前面put的线程获取了,那么take将永远得不到机会执行。怎么回事呢?

后来,我查了condition的await方法,它的注释如下:

  • Causes the current thread to wait until it is signalled or interrupted.
  • The lock associated with this Condition is atomically released and the current thread becomes disabled for thread scheduling purposes and lies dormant until one of four things happens......

原因在await方法的作用上。因为condition是通过lock创建的,而调用condition的await方法时,会自动释放和condition关联的锁。所以说,当put线程被阻塞后,它实际已经释放了锁了。所以,当有take线程想执行时,它是可以获取到锁的。

另一个问题:当等待在condition上的线程被唤醒时,因为之前调用await前,已经获取了锁,那么被唤醒时,它是自动就拥有了锁,还是需要重新获取呢?

在await方法的注释中,有如下的一段话:

  • In all cases, before this method can return the current thread must re-acquire the lock associated with this condition. When the thread returns it is guaranteed to hold this lock.

说明当等待在condition上的线程被唤醒时,它需要重新获取condition关联的锁,获取到之后,await方法才会返回。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • 详细分析Java并发集合ArrayBlockingQueue的用法

    在上一章中,我们介绍了阻塞队列BlockingQueue,下面我们介绍它的常用实现类ArrayBlockingQueue. 一. 用数组来实现队列 因为队列这种数据结构的特殊要求,所以它天然适合用链表的方式来实现,用两个变量分别记录链表头和链表尾,当删除或插入队列时,只要改变链表头或链表尾就可以了,而且链表使用引用的方式链接的,所以它的容量几乎是无限的. 那么怎么使用数组来实现队列,我们需要四个变量:Object[] array来存储队列中元素,headIndex和tailIndex分别记录队列

  • java中LinkedBlockingQueue与ArrayBlockingQueue的异同

    相同: 1.LinkedBlockingQueue和ArrayBlockingQueue都实现了BlockingQueue接口: 2.LinkedBlockingQueue和ArrayBlockingQueue都是可阻塞的队列 内部都是使用ReentrantLock和Condition来保证生产和消费的同步: 当队列为空,消费者线程被阻塞:当队列装满,生产者线程被阻塞: 使用Condition的方法来同步和通信:await()和signal() 不同: 1.由上图可以看出,他们的锁机制不同 Li

  • Java concurrency集合之ArrayBlockingQueue_动力节点Java学院整理

    ArrayBlockingQueue介绍 ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列. 线程安全是指,ArrayBlockingQueue内部通过"互斥锁"保护竞争资源,实现了多线程对竞争资源的互斥访问.而有界,则是指ArrayBlockingQueue对应的数组是有界限的. 阻塞队列,是指多线程访问竞争资源时,当竞争资源已被某线程获取时,其它要获取该资源的线程需要阻塞等待:而且,ArrayBlockingQueue是按 FIFO(先进先出)原则对元素进行

  • java集合框架 arrayblockingqueue应用分析

    Queue ------------ 1.ArrayDeque, (数组双端队列) 2.PriorityQueue, (优先级队列) 3.ConcurrentLinkedQueue, (基于链表的并发队列) 4.DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口) 5.ArrayBlockingQueue, (基于数组的并发阻塞队列) 6.LinkedBlockingQueue, (基于链表的FIFO阻塞队列) 7.LinkedBlockingDeque, (

  • Java源码解析阻塞队列ArrayBlockingQueue功能简介

    本文基于jdk1.8进行分析. 阻塞队列是java开发时常用的一个数据结构.首先看一下阻塞队列的作用是什么.阻塞队列的作用,从源码中类的注释中来了解,是最清晰准确的. ArrayBlockingQueue是一个用数组实现的有界阻塞队列.提供FIFO的功能.队列头上的元素是在队列中呆了最长时间的元素,队列尾上的元素是在队列中呆了时间最短的元素.新元素会插入在队列尾部,从队列获取元素时会从队列头上获取. 这是一个传统的有界队列,在这个有界队列里,一个固定大小的数组用来保存生产者产生的元素和消费者获取

  • 详解ArrayBlockQueue源码解析

    今天要讲的是ArrayBlockQueue,ArrayBlockQueue是JUC提供的线程安全的有界的阻塞队列,一看到Array,第一反应:这货肯定和数组有关,既然是数组,那自然是有界的了,我们先来看看ArrayBlockQueue的基本使用方法,然后再看看ArrayBlockQueue的源码. ArrayBlockQueue基本使用 public static void main(String[] args) throws InterruptedException { ArrayBlocki

  • java并发之ArrayBlockingQueue详细介绍

    java并发之ArrayBlockingQueue详细介绍 ArrayBlockingQueue是常用的线程集合,在线程池中也常常被当做任务队列来使用.使用频率特别高.他是维护的是一个循环队列(基于数组实现),循环结构在数据结构中比较常见,但是在源码实现中还是比较少见的. 线程安全的实现 线程安全队列,基本是离不开锁的.ArrayBlockingQueue使用的是ReentrantLock,配合两种Condition,实现了集合的线程安全操作.这里稍微说一个好习惯,下面是成员变量的声明. pri

  • Java源码解析阻塞队列ArrayBlockingQueue常用方法

    本文基于jdk1.8进行分析 ArrayBlockingQueue的功能简介参考https://www.jb51.net/article/154211.htm. 首先看一下ArrayBlockingQueue的成员变量.如下图.最主要的成员变量是items,它是一个Object类型的数组用于保存阻塞队列中的元素.其次是takeIndex,putIndex,count,分别表示了从队列获取元素的位置,往队列里放元素的位置和队列中元素的个数.然后是lock,notEmpty和notFull三个和锁相

  • Java源码解析阻塞队列ArrayBlockingQueue介绍

    Java的阻塞队列,在实现时,使用到了lock和condition,下面是对其主要方法的介绍. 首先看一下,阻塞队列中使用到的锁. /** Main lock guarding all access **/ final ReentrantLock lock;​ /** Condition for waiting takes **/ private final Condition notEmpty;​ /** Condition for waiting puts **/ private final

  • Java源码解析之详解ReentrantLock

    ReentrantLock ReentrantLock是一种可重入的互斥锁,它的行为和作用与关键字synchronized有些类似,在并发场景下可以让多个线程按照一定的顺序访问同一资源.相比synchronized,ReentrantLock多了可扩展的能力,比如我们可以创建一个名为MyReentrantLock的类继承ReentrantLock,并重写部分方法使其更加高效. 当一个线程调用ReentrantLock.lock()方法时,如果ReentrantLock没有被其他线程持有,且不存在

  • Java源码解析之object类

    在源码的阅读过程中,可以了解别人实现某个功能的涉及思路,看看他们是怎么想,怎么做的.接下来,我们看看这篇Java源码解析之object的详细内容. Java基类Object java.lang.Object,Java所有类的父类,在你编写一个类的时候,若无指定父类(没有显式extends一个父类)编译器(一般编译器完成该步骤)会默认的添加Object为该类的父类(可以将该类反编译看其字节码,不过貌似Java7自带的反编译javap现在看不到了). 再说的详细点:假如类A,没有显式继承其他类,编译

  • Java源码解析之HashMap的put、resize方法详解

    一.HashMap 简介 HashMap 底层采用哈希表结构 数组加链表加红黑树实现,允许储存null键和null值 数组优点:通过数组下标可以快速实现对数组元素的访问,效率高 链表优点:插入或删除数据不需要移动元素,只需要修改节点引用效率高 二.源码分析 2.1 继承和实现 public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {

  • Java源码解析之接口Collection

    一.图示 二.方法定义 我们先想一想,公司如果要我们自己去封装一些操作数组或者链表的工具类,我么需要封装哪些功能呢?不妨就是统计其 大小,增删改查.清空或者是查看否含有某条数据等等.而collection接口就是把这些通常操作提取出来,使其更全面.更通用,那现在我们就来看看其源码都有哪些方法. //返回集合的长度,如果长度大于Integer.MAX_VALUE,返回Integer.MAX_VALUE int size(); //如果集合元素总数为0,返回true boolean isEmpty(

  • Java源码解析之平衡二叉树

    一.平衡二叉树的定义 平衡二叉树是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1 .它是一种高度平衡的二叉排序树.意思是说,要么它是一棵空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1 .我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF (Balance Factor),那么平衡二叉树上所有结点的平衡因子只可能是-1 .0 和1. 这里举个栗子: 仔细看图中值为18的节点,18的节点的深度为2 .而它的右子树的深度为0,其差

  • Java源码解析之接口List

    前言 List接口是Collection接口的三大接口之一,其中的数据可以通过位置检索,用户可以在指定位置插入数据.List的数据可以为空,可以重复.我们来看看api文档是怎么说的: 一.List特有的方法 我们这里就只关注和Collection不同的方法,主要有以下这些: //在指定位置,将指定的集合插入到当前的集合中 boolean addAll(int index, Collection<? extends E> c); //这是一个默认实现的方法,会通过Iterator的方式对每个元素

  • Java源码解析之超级接口Map

    前言 我们在前面说到的无论是链表还是数组,都有自己的优缺点,数组查询速度很快而插入很慢,链表在插入时表现优秀但查询无力.哈希表则整合了数组与链表的优点,能在插入和查找等方面都有不错的速度.我们之后要分析的HashMap就是基于哈希表实现的,不过在JDK1.8中还引入了红黑树,其性能进一步提升了. 今天我们来说一说超级接口Map. 一.接口Map Map是基于Key-Value的数据格式,并且key值不能重复,每个key对应的value值唯一.Map的key也可以为null,但不可重复. 在看Ma

随机推荐