目录
什么是阻塞队列
阻塞队列(BlockingQueue)是一个附加支持阻塞的插入和移除的队列。
-
支持阻塞的插入方法:当队列满时,队列会阻塞插入元素的线程,直到队列不满。
-
支持阻塞的移除方法:当队列为空时,队列会阻塞插入元素的线程,知道队列变为非空。
阻塞队列支持的方法
方法/处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除方法 | remove() | poll() | take() | poll(time,unit) |
检查方法 | element() | peek() | 不可用 | 不可用 |
注:
-
抛出异常:当队列满时,如果再往队列中插入元素,会抛出IllegalStateExeception("Queue full")异常,当队列空时,从队列里获取元素会抛出NoSuchElementException异常。
-
返回特殊值:当往队列插入元素时,会返回元素是否插入成功,成功返回true。如果是移除方法,则是从队列里取出一个元素,如果没有则返回null。
-
一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断退出。当队列空时,如果消费者线程从队列里take元素,队列会阻塞住消费者线程,直到队列不为空。
-
超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过了指定的时间,生产者线程就会退出。当队列空时,如果消费者线程从队列里移除元素,队列会阻塞消费者线程一段时间,如果超过了指定的时间,消费者线程就会退出。
阻塞队列的实现原理
阻塞队列的实现方式的是通知者模式。通知者模式是一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
不同的阻塞队列实现方式不同,以ArrayBlockingQueue为例,剖析阻塞队列的实现。ArrayBlockingQueue使用Condition实现:
public class ArrayBlockingQueue<E> {
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
public ArrayBlockingQueue(int capacity, boolean fair) {
//初始化lock和Condition
this.lock = new ReentrantLock(fair);
this.notEmpty = this.lock.newCondition();
this.notFull = this.lock.newCondition();
}
public void put(E e) throws InterruptedException {
ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while(this.count == this.items.length) {
//如果队列满,等待
this.notFull.await();
}
this.enqueue(e);
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
ReentrantLock lock = this.lock;
lock.lockInterruptibly();
Object var2;
try {
while(this.count == 0) {
//如果队列为空,等待
this.notEmpty.await();
}
var2 = this.dequeue();
} finally {
lock.unlock();
}
return var2;
}
private void enqueue(E e) {
//插入元素,通知等待的消费者
this.notEmpty.signal();
}
private E dequeue() {
//移除元素,通知等待的生茶者
this.notFull.signal();
return e;
}
}
1.Condition
通知者模式的应用是在Condition的实现上,Condition维护了一个FIFO(First Input First Output)等待队列,队列的每个节点都包含了一个线程的引用。Condition是由Lock创建的,Lock可以有多个Condition对象,所以Lock有一个同步队列和多个等待队列。Object监视器模型(Monitor),一个对象只有一个同步队列和一个等待队列。
Conditon拥有首尾节点的引用,当线程调用Condition.await()方法,将会阻塞线程,以当前线程构造节点(同步器的静态内部类AbstractQueuedSynchronizer.Node),并将节点从尾部加入等待队列。当调用Condition.singal()方法,会将Condition等待队列中的首节点移到Lock()同步队列中,然后唤醒线程。当调用Condition.singalAll()方法,相当于对等待队列的每个节点做一次singal()方法。
public class ConditionObject implements Condition {
private transient AbstractQueuedSynchronizer.Node firstWaiter;
private transient AbstractQueuedSynchronizer.Node lastWaiter;
public final void signal() {
AbstractQueuedSynchronizer.Node first = this.firstWaiter;
if (first != null) {
//唤醒同步队列首节点,调用LockSupport.unpark()
this.doSignal(first);
}
}
public final void signalAll() {
AbstractQueuedSynchronizer.Node first = this.firstWaiter;
if (first != null) {
//唤醒同步队列全部节点,调用LockSupport.unpark()
this.doSignalAll(first);
}
}
public final void await() throws InterruptedException {
//当前线程加入等待队列
AbstractQueuedSynchronizer.Node node = this.addConditionWaiter();
//释放锁
int savedState = AbstractQueuedSynchronizer.this.fullyRelease(node);
int interruptMode = 0;
while (!AbstractQueuedSynchronizer.this.isOnSyncQueue(node)) {
//阻塞当前线程
LockSupport.park(this);
if ((interruptMode = this.checkInterruptWhileWaiting(node)) != 0) {
break;
}
}
}
}
2.LockSupport
LockSuopport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而LockSupport也成为构建同步组件的基础工具。LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。通过调用Unsafe的虚拟机方法来完成阻塞和唤醒操作。
在jdk1.6中,LockSupport增加了park(Object blocker)、parkNanos(Obejcet blocker, long nanos)、parkUtil(Object blocker, long deadline)。其中参数blocker是用来标识当前线程在等待的对象,该对象主要用于问题排查和系统监控。
方法名称 | 描述 |
---|---|
void park() | 阻塞当前线程,如果调用unpark(Thread thread)方法或者当前线程被中断,才能从park()方法返回。 |
void parkNanos(long nanos) | 阻塞当前线程,最长不超过nanos秒,超时返回。 |
void parkUntil(long deadline) | 阻塞当前线程,直到deadline时间(从1970年开始到deadline时间毫秒数) |
void unpark(Thread thread) | 唤醒处于阻塞状态的线程thread。 |
/**
* LockSupport类
**/
public class LockSupport {
private static final Unsafe U = Unsafe.getUnsafe();
private static final long PARKBLOCKER;
private static final long SECONDARY;
private static final long TID;
private LockSupport() {
}
private static void setBlocker(Thread t, Object arg) {
U.putObject(t, PARKBLOCKER, arg);
}
public static void unpark(Thread thread) {
if (thread != null) {
U.unpark(thread);
}
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, 0L);
setBlocker(t, (Object)null);
}
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0L) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, nanos);
setBlocker(t, (Object)null);
}
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(true, deadline);
setBlocker(t, (Object)null);
}
public static Object getBlocker(Thread t) {
if (t == null) {
throw new NullPointerException();
} else {
return U.getObjectVolatile(t, PARKBLOCKER);
}
}
public static void park() {
U.park(false, 0L);
}
public static void parkNanos(long nanos) {
if (nanos > 0L) {
U.park(false, nanos);
}
}
public static void parkUntil(long deadline) {
U.park(true, deadline);
}
static final int nextSecondarySeed() {
Thread t = Thread.currentThread();
int r;
if ((r = U.getInt(t, SECONDARY)) != 0) {
r ^= r << 13;
r ^= r >>> 17;
r ^= r << 5;
} else if ((r = ThreadLocalRandom.current().nextInt()) == 0) {
r = 1;
}
U.putInt(t, SECONDARY, r);
return r;
}
static final long getThreadId(Thread thread) {
return U.getLong(thread, TID);
}
static {
PARKBLOCKER = U.objectFieldOffset(Thread.class, "parkBlocker");
SECONDARY = U.objectFieldOffset(Thread.class, "threadLocalRandomSecondarySeed");
TID = U.objectFieldOffset(Thread.class, "tid");
}
}
Java中的阻塞队列
- ArrayBlockingQueue:一个用数组实现的有界阻塞队列。按照FIFO(First Input First Output)原则对元素进行排序,默认非公平。
- LinkedBlockingQueue:一个用链表实现的有界阻塞队列,此队列的默认和最大长度为Integer.MAX_VALUE。按照FIFO的原则对元素进行排序。
- PriorityBlockingQueue:一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序升序排列,可以实现Comparator接口来进行排序。
- DelayQueue:一个支持延时获取元素的无界阻塞队列,队列使用PriorityQueue实现,队列中元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素,只有延迟期满时才能从队列中提取元素。
- SynchronousQueue:一个不存储元素的阻塞队列,每一个put操作必须等待一个take操作,否则不能添加元素。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。transfer方法,如果当前有消费者正在等待接收元素(消费者使用take()或带时间显示的poll()方法时),transfet方法可以把生产者传入的元素立刻transfer(传输)给消费者。如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。多了XXFirst和XXLast方法