ArrayBlockingQueue源码解析,付出入队流程图

概述

ArrayBlockingQueue是一个基于数组实现的有界阻塞队列。遵循先进先出FIFO原则对元素进行排序,元素都是从尾部入队,头部出队。

1、数据结构

基于数组的阻塞队列,必须设置长度,元素不允许为空,先进先出。

2、原理

ArrayBlockingQueue,读写用一把锁,也就意味着所有加锁操作都是互斥的。当队列已满时,put线程会阻塞,当队列为空时,take线程会阻塞。

3、源码解读

3.1 变量

ArrayBlockingQueue阻塞队列中定义存储数据的数组item、下一个要取出元素的索引takeIndex、下一个要加入的元素索引putIndex、队列中元素的个数count,一把锁lock、读写对应的两个condition对象notFull 、notEmpty以及维护数组序列的itrs。

3.2 构造方法

ArrayBlockingQueue 的构造方法有三个,分别如下:

  • 设置容量大小,未指定是否使用公平锁,默认使用非公平锁;
  • 设置容量大小,且指定锁类型;
  • 将Collection集合中的元素导入队列,其实用的就是put(E x)方法,后面将put方法时详述,这里不做详细介绍。

3.3 put(E x)入队方法

入队按顺序,队列已满,则进入入队等待队列等待。

public void put(E e) throws InterruptedException {  
		//元素判空
        checkNotNull(e);  
        final ReentrantLock lock = this.lock;  
        //获取独占锁,和lock()方法的区别是让它在阻塞时也可抛出异常跳出  
        lock.lockInterruptibly();
        try {  
        	//如果队列已满,进入入队等待线程队列等待
            while (count == items.length)  
                notFull.await();  
            //入队,详见下面的入队操作
            enqueue(e);  
        } finally {  
        	//释放锁
            lock.unlock();  
        }  
    }  
  private void enqueue(E x) {  
        final Object[] items = this.items;  
        items[putIndex] = x;  
        //队列已满,设置putIndex为0
        if (++putIndex == items.length)  
            putIndex = 0;  
        count++;//队列中元素个数加1  
        notEmpty.signal();//通知出队等待队列中的线程,队列不为空 
    }  

3.4 take(E x)出队方法

出队按顺序,队列为空,则进入出队等待线程队列等待。

public E take() throws InterruptedException {
	final ReentrantLock lock = this.lock;
	lock.lockInterruptibly();
	try {
		//如果队列为空,则进入出队等待线程队列等待
	    while (count == 0)
	        notEmpty.await();
	       // 详见下面的出队方法
	    return dequeue();
	} finally {
	    lock.unlock();
	}
	}
private E dequeue() {  
        final Object[] items = this.items;  
        @SuppressWarnings("unchecked")  
        E x = (E) items[takeIndex];  
        items[takeIndex] = null;  
       //队列中最后一个元素取走,则从0号位开始取
        if (++takeIndex == items.length)  
            takeIndex = 0;  
        count--;/当前拥有元素个数减1  
       // 维护itrs参数,比较复杂,有兴趣的可以深入研究
        if (itrs != null)  
            itrs.elementDequeued();  
          //通知入队等待队列中的线程,队列中有位置空出来了  
        notFull.signal();
        return x;  
    }  

3.4 迭代器

由于数组带有下标,迭代器也维护了元素的位置索引,两者有关联关系,所以每次去除元素后,都需要维护Iterator迭代器中的参数。有兴趣的可以深入研究迭代器原理。

总结

ArrayBlockingQueue相对来讲比较简单,维护itrs部分比较难,有兴趣的可以研究。其特点如下:

  • 元素不允许为空,为空会抛NullPointException异常;
  • 有界队列,必须设置容量大小;
  • 底层数据结构为数组;
  • 读写用同一把锁,所以是安全类
  • 读写同一把锁,也就说明了读写不能同时进行,但由于其基于数组实现,天然的效率很高;
  • 每次take操作都需维护Iterator迭代器中的参数;

你可能感兴趣的:(并发编程,队列,数据结构,java,多线程,并发编程)