JDK容器与并发—Queue—ArrayBlockingQueue

概述

      基于数组的有界阻塞队列。

1)FIFO;

2)ArrayBlockingQueue是经典的有界缓存的实现,其内是固定大小的数组缓存,生产者向其中插入元素,消费者从其取出元素;

3)支持公平锁,默认为非公平锁,公平锁会降低吞吐量但更稳定,避免某个线程阻塞不工作的情况;

数据结构

      Object数组,逻辑上是环形数组


/** The queued items */
final Object[] items;

/** items index for next take, poll, peek or remove */
int takeIndex;

/** items index for next put, offer, or add */
int putIndex;

/** Number of elements in the queue */
int count;

// 经典的一把锁两个条件的并发控制
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;

构造器

// 带容量参数构造,默认为非公平锁
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(); // Queue非空
	notFull =  lock.newCondition(); // Queue未满
}

// 带容量、是否为公平锁参数、Collection参数构造
// 将Collection中的元素添加进来
public ArrayBlockingQueue(int capacity, boolean fair,
						  Collection<? extends E> c) {
	this(capacity, fair);

	final ReentrantLock lock = this.lock;
	lock.lock(); // 为可见性加锁,非互斥
	try {
		int i = 0;
		try {
			for (E e : c) {
				checkNotNull(e);
				items[i++] = e;
			}
		} catch (ArrayIndexOutOfBoundsException ex) {
			throw new IllegalArgumentException();
		}
		count = i;
		putIndex = (i == capacity) ? 0 : i;
	} finally {
		lock.unlock();
	}
}

增删查

基础方法

// 循环增加
final int inc(int i) {
	return (++i == items.length) ? 0 : i;
}

// 将元素循环入队
private void insert(E x) {
	items[putIndex] = x;
	putIndex = inc(putIndex);
	++count;
	notEmpty.signal(); // 发送队列非空信号
}

// 将元素循环出队
private E extract() {
	final Object[] items = this.items;
	E x = this.<E>cast(items[takeIndex]);
	items[takeIndex] = null;
	takeIndex = inc(takeIndex);
	--count;
	notFull.signal(); // 发送队列未满信号
	return x;
}

      步骤:

1)竞争获取lock;

2)若队列未满,则才将元素入队,发送队列非空信号;

3)若队列已满,offer立即返回false,add立即抛出IllegalStateException异常,put一直等到队列未满,offer阻塞版则等待timeout时间,超出则立即返回false;

4)释放锁。

// offer的抛出异常版本IllegalStateException
public boolean add(E e) {
	return super.add(e);
}

// 将元素e队列
// 非阻塞
public boolean offer(E e) {
	checkNotNull(e);
	final ReentrantLock lock = this.lock;
	lock.lock();
	try {
		if (count == items.length) // 队列已满,立即返回false
			return false;
		else {
			insert(e);             // 队列未满,将元素入队,返回true
			return true;
		}
	} finally {
		lock.unlock();
	}
}

// 将元素e队列
// 无限阻塞
public void put(E e) throws InterruptedException {
	checkNotNull(e);
	final ReentrantLock lock = this.lock;
	lock.lockInterruptibly();          // 获取到锁前,可中断的锁的获取
	try {
		while (count == items.length)  // 队列已满,则一直wait
			notFull.await();
		insert(e);					   // 直到等到队列未满时,才将元素入队,发送队列非空信号
	} finally {
		lock.unlock();
	}
}

// 将元素e队列
// 有限阻塞,时长为timeout
 public boolean offer(E e, long timeout, TimeUnit unit)
	throws InterruptedException {

	checkNotNull(e);
	long nanos = unit.toNanos(timeout);
	final ReentrantLock lock = this.lock;
	lock.lockInterruptibly();                  // 获取到锁前,可中断的锁的获取
	try {
		while (count == items.length) {        // 队列已满,则等待timeout,超出则立即返回
			if (nanos <= 0)
				return false;
			nanos = notFull.awaitNanos(nanos);
		}
		insert(e);							   // 等待小于timeout,则将元素入队,发送队列非空信号
		return true;
	} finally {
		lock.unlock();
	}
}

      步骤:
1)竞争获取lock;
2)若队列非空,则将队首元素出队,发送队列未满信号;
3)若队列为空,poll立即返回null,remove立即抛出NoSuchElementException异常,take一直等到队列非空,poll阻塞版则等待timeout时间,超出则立即返回null;
4)释放锁。

// 将队首元素出队
// 非阻塞
 public E poll() {
	final ReentrantLock lock = this.lock;
	lock.lock();
	try {
		return (count == 0) ? null : extract(); // 队列为空,返回null;否则将队首元素出队,发送队列未满信号
	} finally {
		lock.unlock();
	}
}

// 将队首元素出队
// 无限阻塞
public E take() throws InterruptedException {
	final ReentrantLock lock = this.lock;
	lock.lockInterruptibly();             // 获取到锁前,可中断的锁的获取
	try {
		while (count == 0)				  // 队列为空,则一直wait
			notEmpty.await();
		return extract();				  // 直到等到队列非空时,才将队首元素出队,发送队列未满信号
	} finally {
		lock.unlock();
	}
}

// 将队首元素出队
// 有限阻塞
 public E poll(long timeout, TimeUnit unit) throws InterruptedException {
	long nanos = unit.toNanos(timeout);
	final ReentrantLock lock = this.lock;
	lock.lockInterruptibly();					// 获取到锁前,可中断的锁的获取
	try {
		while (count == 0) {					// 队列为空,则等待timeout,超出则立即返回null
			if (nanos <= 0)
				return null;
			nanos = notEmpty.awaitNanos(nanos);
		}
		return extract();						// 等待小于timeout,则将将队首元素出队,发送队列未满信号
	} finally {
		lock.unlock();
	}
}

      步骤:
1)竞争获取lock;
2)队列为空,返回null;否则队首元素;
3)释放锁。

// 获取队首元素
public E peek() {
	final ReentrantLock lock = this.lock;
	lock.lock();           // 获取lock
	try {
		return (count == 0) ? null : itemAt(takeIndex); // 队列为空,返回null;否则队首元素
	} finally {
		lock.unlock();     // 释放lock
	}
}

迭代器

      弱一致性。

/**
 * Iterator for ArrayBlockingQueue. To maintain weak consistency
 * with respect to puts and takes, we (1) read ahead one slot, so
 * as to not report hasNext true but then not have an element to
 * return -- however we later recheck this slot to use the most
 * current value; (2) ensure that each array slot is traversed at
 * most once (by tracking "remaining" elements); (3) skip over
 * null slots, which can occur if takes race ahead of iterators.
 * However, for circular array-based queues, we cannot rely on any
 * well established definition of what it means to be weakly
 * consistent with respect to interior removes since these may
 * require slot overwrites in the process of sliding elements to
 * cover gaps. So we settle for resiliency, operating on
 * established apparent nexts, which may miss some elements that
 * have moved between calls to next.
 */
private class Itr implements Iterator<E> {
	private int remaining; // Number of elements yet to be returned
	private int nextIndex; // Index of element to be returned by next
	private E nextItem;    // Element to be returned by next call to next
	private E lastItem;    // Element returned by last call to next
	private int lastRet;   // Index of last element returned, or -1 if none

	Itr() {
		final ReentrantLock lock = ArrayBlockingQueue.this.lock;
		lock.lock();
		try {
			lastRet = -1;
			if ((remaining = count) > 0)
				nextItem = itemAt(nextIndex = takeIndex);
		} finally {
			lock.unlock();
		}
	}

	public boolean hasNext() {
		return remaining > 0;
	}

	public E next() {
		final ReentrantLock lock = ArrayBlockingQueue.this.lock;
		lock.lock();
		try {
			if (remaining <= 0)
				throw new NoSuchElementException();
			lastRet = nextIndex;
			E x = itemAt(nextIndex);  // check for fresher value
			if (x == null) {
				x = nextItem;         // we are forced to report old value
				lastItem = null;      // but ensure remove fails
			}
			else
				lastItem = x;
			while (--remaining > 0 && // skip over nulls
				   (nextItem = itemAt(nextIndex = inc(nextIndex))) == null)
				;
			return x;
		} finally {
			lock.unlock();
		}
	}

	public void remove() {
		final ReentrantLock lock = ArrayBlockingQueue.this.lock;
		lock.lock();
		try {
			int i = lastRet;
			if (i == -1)
				throw new IllegalStateException();
			lastRet = -1;
			E x = lastItem;
			lastItem = null;
			// only remove if item still at index
			if (x != null && x == items[i]) {
				boolean removingHead = (i == takeIndex);
				removeAt(i);
				if (!removingHead)
					nextIndex = dec(nextIndex);
			}
		} finally {
			lock.unlock();
		}
	}
}

特性

       数组+有界+阻塞队列。

ArrayBlockingQueue用一把ReentrantLock锁,两个条件notEmpty、notFull,以让增加、删除操作之间互相协作,保证队列中有元素可取、有空间可插入,实现相对于缓存的功能。而在ConcurrentHashMap中增、删、改之间是互斥的,它们的并发意义是不一样的。

疑问

      数组的可见性如何在并发中体现出来?ConcurrentHashMap、ArrayBlockingQueue各不相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值