ArrayBlockingQueue源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fengluoye2012/article/details/82807445

简介

队列(Queue)的实现方式主要有两种,一种是以数组来实现的;另一种是以链表的来实现的。

ArrayBlockingQueue是以固定长度的数组实现的,先进先出;head是队列中存在最久的元素,tail是最新加入的元素,添加元素从队尾添加;删除元素,从队头删除;
不容许插入null; 线程安全,用ReentrantLock实现线程安全;

final Object[] items:元素数组;
int takeIndex:队列队头元素在数组items的下标;
int putIndex:队列队尾元素在数组items的下标+1(即插入当前对象在items中的下标)
int count:队列中元素的个数;

takeIndex和putIndex需要特别注意,ArrayBlockingQueue以固定长度的数组来实现的关键思想;

队列中第X个元素和items数组中存储元素下标,不存在对应关系;有可能队列第X个元素,在items数组中的下标为0,1,2,等情况;

构造函数

构造函数:capacity指定队列固定容量,无法再次更改;默认情况下,默认是不公平的,创建不公平重入锁;

public ArrayBlockingQueue(int capacity) {
    this(capacity, false);
}

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();
}

插入元素:add(),offer(),put();依次插入到队列;

add():是否插入成功;如果队列容量未满,直接插入;否则报异常队列已满;

public boolean add(E e) {
    return super.add(e);
}

public boolean add(E e) {
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}

offer()方法:是否插入成功;先非空检查;插入过程是线程同步的;如果队列已满,返回false;队列未满时,向队列中插入数据,返回true;

 public boolean offer(E e) {
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
		//判断队列count和数组items的length是否相等,如果相等,则队列已满;
        if (count == items.length)
            return false;
        else {
            enqueue(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}

enqueue():插入数据,

private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
	//插入之后,putIndex和items数组length相等,则数组尾部已经全部插入元素;将putIndex赋值为0,看看数组头部是否还有空间(因为插入时putIndex从0开始增加;);
    if (++putIndex == items.length) putIndex = 0;
    count++;
	//唤醒一个在await()队列中的线程
    notEmpty.signal();
}

put():队列未满,直接插入;队列已满情况下,线程等待;

public void put(E e) throws InterruptedException {
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;
	//当前线程未被中断,则获取锁;如果已经中断则,抛出异常;
    lock.lockInterruptibly();
    try {
		//如果队列已慢,等待;
        while (count == items.length)
            notFull.await();
		//插入数据
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

获取元素;element(),peek()

element()方法,主要调用peek()方法

public E element() {
    E x = peek();
    if (x != null)
        return x;
    else
        throw new NoSuchElementException();
}

peek():根据takeIndex从数组items中取得对应元素;takeIndex默认是0;在删除操作之后,takeIndex+1;

 public E peek() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return itemAt(takeIndex); // null when queue is empty
    } finally {
        lock.unlock();
    }
}

final E itemAt(int i) {
    return (E) items[i];
}

删除元素:poll(),take(),remove()

poll():如果队列count== 0,直接返回null;队列count>0;返回被删除元素;从队列头开始删除元素;

public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return (count == 0) ? null : dequeue();
    } finally {
        lock.unlock();
    }
}

dequeue():删除takeIndex对应的元素,

private E dequeue() {
    // assert lock.getHoldCount() == 1;
    // assert items[takeIndex] != null;
    final Object[] items = this.items;
	//takeIndex
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];
    items[takeIndex] = null;
	//删除之后,takeIndex等于数组length,说明数组items尾部元素,全部删除,看看数组items头部是否还有元素存在;
    if (++takeIndex == items.length) takeIndex = 0;
    count--;
    if (itrs != null)
        itrs.elementDequeued();
	//唤醒一个await()队列中的线程;
    notFull.signal();
    return x;
}

elementDequeued()

 void elementDequeued() {
        // assert lock.getHoldCount() == 1;
        if (count == 0)
            queueIsEmpty();
        else if (takeIndex == 0)
            takeIndexWrapped();
    }

take():当队列为空时,线程等待;队列不为空,直接返回被删除元素;

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

remove():是否删除成功

public boolean remove(Object o) {
	//删除的元素不能为null;应为插入时,非空检查,队列中没有null对象;
    if (o == null) return false;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count > 0) {
            final Object[] items = this.items;
            final int putIndex = this.putIndex;
            int i = takeIndex;
			//从takeIndex到putIndex范围之间循环,如果队列中存在对象o,找到对应的index,删除;不存在直接返回false;
            do {
				//队列中存在对象o;
                if (o.equals(items[i])) {
                    removeAt(i);
                    return true;
                }
                if (++i == items.length) i = 0;
            } while (i != putIndex);
        }
        return false;
    } finally {
        lock.unlock();
    }
}

removeAt():

void removeAt(final int removeIndex) {
    // assert lock.getHoldCount() == 1;
    // assert items[removeIndex] != null;
    // assert removeIndex >= 0 && removeIndex < items.length;
    final Object[] items = this.items;
	//相等话,即队尾删除,和其他两种删除方式一致;直接从数组中删除,takeIndex+1;count-1;
    if (removeIndex == takeIndex) {
        // removing front item; just advance
        items[takeIndex] = null;
        if (++takeIndex == items.length) takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
    } else {
        // an "interior" remove

        // slide over all others up through putIndex.
        for (int i = removeIndex, putIndex = this.putIndex;;) {
            int pred = i;
			//如果到数组items尾部,从0开始,继续循环查找;
            if (++i == items.length) i = 0;
			//直到要移动的下标为putIndex时,将下标pred对应的元素置空,并且将putIndex赋值为pred;退出循环;
            if (i == putIndex) {
                items[pred] = null;
                this.putIndex = pred;
                break;
            }
			//从数组items删除removeIndex对应的元素,然后将从removeIndex+1开始的元素,全部往前移一个位置;
            items[pred] = items[i];
        }
		//队列count减一;
        count--;
        if (itrs != null)
            itrs.removedAt(removeIndex);
    }
	//唤醒一个await()队列中的线程;
    notFull.signal();
}

以上就是ArrayBlockingQueue类的主要方法分析;如有问题,请多指教,谢谢!

猜你喜欢

转载自blog.csdn.net/fengluoye2012/article/details/82807445