1.阻塞队列是一个增加了2个附加操作的队列,这2个操作是:当队列中元素为空时,获取元素线程会等待队列不为空,当队列满时,存储线程会等待队列中有空闲才会操作.这种操作常用于生产者和消费者的场景。
阻塞队列提供了四种处理方法:
方法\处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除方法 | remove() | poll() | take() | poll(time,unit) |
检查方法 | element() | peek() | 不可用 | 不可用 |
抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException(“Queue full”)异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。
返回特殊值:插入方法会返回是否成功,成功则返回true。移除方法,则是从队列里拿出一个元素,如果没有则返回null
一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。
2. Java里的阻塞队列
JDK7提供了7个阻塞队列。分别是
ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
这里主要分析ArrayBlockingQueue
, LinkedBlockingQueue
,SynchronousQueue
3.ArrayBlockingQueue队列
(1)数据结构采用的是数组,对数组的访问添加了锁的机制,使其能够支持多线程并发。
(2)类的继承关系
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
}
(3)类的属性
final Object[] items;//队列中的元素,采用Object数组
int takeIndex;//取元素的索引
int putIndex;//存储元素的索引
int count;//队列中的元素的个数
/**可重入锁 */
final ReentrantLock lock;
/** 获取元素的等待状态 */
private final Condition notEmpty;
/** 存储元素的等待状态 */
private final Condition notFull;
(4)构造方法
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);//根据fair来创建公平锁或非公平锁
//创建2个状态,目的实现阻塞
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;//put元素的位置
} finally {
lock.unlock();
}
}
(5)部分方法
(5.1)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();//释放锁
}
}
private void insert(E x) {
items[putIndex] = x;//将元素放到队列中
putIndex = inc(putIndex);//增加取元素的标志
++count;//增加元素的数量
notEmpty.signal();//唤醒获取等待
}
(5.2)offer方法
public boolean offer(E e) {
checkNotNull(e);//判断元素是否为空
final ReentrantLock lock = this.lock;
lock.lock();//获取锁
try {
if (count == items.length)
return false;//但队列满时,返回false
else {
insert(e);//插入队列,方法true
return true;
}
} finally {
lock.unlock();//释放锁
}
}
(5.3)take方法
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();//但队列为空时,阻塞取线程
return extract();//取元素
} finally {
lock.unlock();//释放锁
}
}
private E extract() {
final Object[] items = this.items;
E x = this.<E>cast(items[takeIndex]);//取元素
items[takeIndex] = null;//变为null
takeIndex = inc(takeIndex);取位置后移
--count;数量减少
notFull.signal();//唤醒存线程
return x;//返回取的元素
}
(5.4)poll方法
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//如果队列中元素的数量为0时,返回null,否则返回元素
return (count == 0) ? null : extract();
} finally {
lock.unlock();
}
}
(5.5)clear方法
public void clear() {
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
//清空所有元素
for (int i = takeIndex, k = count; k > 0; i = inc(i), k--)
items[i] = null;
//将所有位置重置0
count = 0;
putIndex = 0;
takeIndex = 0;
notFull.signalAll();//唤醒所有的存储线程
} finally {
lock.unlock();
}
}
(5.6)checkNotNull方法,可以知道队列中不能有null的元素
private static void checkNotNull(Object v) {
if (v == null)
throw new NullPointerException();
}
4.实例
package thread2;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class PutTask extends Thread
{
private BlockingQueue<Integer> queue;
public PutTask(BlockingQueue<Integer> queue){
this.queue = queue;
}
public void run()
{
try{
for(int i=1; i<=5; i++){
System.out.println("put "+i);
queue.put(i);
Thread.sleep(10);//等待
}
}catch(Exception e){
e.printStackTrace();
}
}
}
class TakeTask extends Thread
{
private BlockingQueue<Integer> queue;
public TakeTask(BlockingQueue<Integer> queue){
this.queue = queue;
}
public void run()
{
try{
for(int i=1; i<=5; i++){
int temp = queue.take();
System.out.println("take : "+temp);
Thread.sleep(10);//等待
}
}catch(Exception e){
e.printStackTrace();
}
}
}
public class ArrayBlockTest
{
public static void main(String[] args)
{
BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(5);
TakeTask take = new TakeTask(queue);
PutTask put = new PutTask(queue);
take.start();
put.start();
}
}
结果:
put 1
take : 1
put 2
take : 2
put 3
take : 3
put 4
take : 4
put 5
take : 5
参考资料
http://www.infoq.com/cn/articles/java-blocking-queue/
http://www.cnblogs.com/leesf456/p/5533770.html