1.结构
队列是元素的序列,在队列中:
(1) 只能在队列尾进行插入
(2) 只能在队列头进行删除,获取和修改
队列的这种定义有时被称为”先来先服务”.在Java中也实现了队列,先看一下结构吧.
在这里,我只看了这3个类,分别是数组阻塞队列,优先级队列,优先级阻塞队列.
2.ArrayBlockingQueue
2.1 字段
private static final long serialVersionUID = -817911632652898426L;
/** 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;
/*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
serialVersionUID:序列化ID
items:队列的元素集合
takeIndex:队列头下表
putIndex:队列尾下表
count:队列的长度
lock,notEmpty,notFull:和线程相关,用于阻塞.
2.2 构造器
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();
}
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
lock.lock(); // Lock only for visibility, not mutual exclusion
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();
}
}
第三个构造器中如果capacity < c.size()时,会抛出IllegalArgumentException异常.其中关于ReentrantLock类和Condition类可以看这篇博客:http://blog.csdn.net/ghsau/article/details/7481142
2.3 add方法
public boolean add(E e) {
return super.add(e);
}
super.add方法如下:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
接着看子类实现的offer方法:
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
}
该方法是同步的,如果已经满了,return false,否则insert(e).
insert方法如下:
private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex);
++count;
notEmpty.signal();
}
final int inc(int i) {
return (++i == items.length) ? 0 : i;
}
在这里putIndex和takeIndex相当于指针,假如items = new Object[4];items.length=4,此时:
当add(“2”),add(“3”),add(“4”),remove()这3个操作后,如下:
再add(“haha”),add(“hehe”)后,如下:
继续add方法,可以看出inc方法是一个循环,在索引0位置的元素跟随在索引items最大元素的后面,它的好处是add操作只需要常量时间.而不用扩展数组.
其中的notEmpty.signal()方法作用是唤醒读线程,也相当于说”我有数据了,可以来取了”.
2.4 put方法
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}
put方法与add方法最大的不同是:当count == items.length时,add方法选择返回false,提示已经满了.而put方法是使用阻塞,notFull.await()方法表示阻塞写线程 ,当读线程读完后执行notFull.signal() 使该方法执行.
2.5 take方法
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}
notEmpty.await()方法是阻塞读线程.
extract方法如下:
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;
}
读线程读完后执行notFull.signal() 唤醒写进程.
3.PriorityQueue
3.1 字段
private static final long serialVersionUID = -7720805057305804111L;
private static final int DEFAULT_INITIAL_CAPACITY = 11;
private transient Object[] queue;
private int size = 0;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* The comparator, or null if priority queue uses elements'
* natural ordering.
*/
private final Comparator<? super E> comparator;
/**
* The number of times this priority queue has been
* <i>structurally modified</i>. See AbstractList for gory details.
*/
private transient int modCount = 0;
与ArrayBlockingQueue最大的区别是拥有一个比较器comparator.
3.2 构造器
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
public PriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
使用默认的构造器是queue大小为DEFAULT_INITIAL_CAPACITY = 11.
3.3 add方法(offer方法)
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
queue[0] = e;
else
siftUp(i, e);
return true;
}
grow方法用于扩展数组,方法如下:
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// overflow-conscious code
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}
该方法和ArrayList中的扩展数组方法十分相似,不过这里是数组长度超过64时扩展一半,而ArrayList是始终扩展一半.
siftUp方法如下:
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = x;
}
private void siftUpComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
如果有比较器的话,直接使用comparator.compare(x, (E) e),而没有的话使用key的默认构造器.
下面的while方法使用的二分法查找插入的位置,其中的int parent = (k - 1) >>> 1;就是将k-1无符号的向右移一位.
4.PriorityBlockingQueue
该类就是PriorityQueue类中加入ReentrantLock类和Condition类,实现方式和ArrayBlockingQueue十分相似.