本文翻译自http://tutorials.jenkov.com/java-util-concurrent/blockingqueue.html,机翻加人工校正,仅供学习交流。
Java BlockingQueue接口
Java BlockingQueue接口表示一个线程安全的队列,可以将元素放入,也可以取出。换句话说,多个线程可以同时从Java Bloc中插入和获取元素,不会有任何并发性问题。
阻塞队列这个术语就来自于Java BlockingQueue 接口能够阻塞试图在队列插入或获取元素的线程。如果一个线程尝试获取一个元素,而队列中没有剩余的,线程将被阻塞,直到有一个元素可以获取。调用线程是否被阻塞取决于你在BlockingQueue上调用什么方法。稍后将更详细地解释不同的方法。
本文将不讨论如何自己在Java中实现一个BlockingQueue。如果你对此感兴趣,我在更理论化的Java并发教程中有一篇关于阻塞队列的文章。
BlockingQueue实现类
因为BlockingQueue是一个接口,所以您需要使用它的一个实现来使用它。concurrent包有以下BlockingQueue接口的实现:
ArrayBlockingQueue
DelayQueue
LinkedBlockingQueue
LinkedBlockingDeque
LinkedTransferQueue
PriorityBlockingQueue
SynchronousQueue
点击列表中的链接,阅读关于每个实现的更多信息。
BlockingQueue用法
BlockingQueue通常用于让一个线程产生对象,另一个线程使用它。下面的图表说明了这一原理:
生产线程将继续产生新的对象,并将它们插入到BlockingQueue中,直到队列达到它所能包含内容的某个上限,换句话说,这是极限。当阻塞队列达到其上限,生产线程在试图插入新对象时被阻塞。它将一直处于阻塞状态,直到一个正在使用的线程将一个对象从队列中取出。
消费线程不断地从BlockingQueue中取出对象来处理它们,如果使用线程试图从空队列中取出一个对象,消费线程被阻塞,直到生产线程将对象放入队列。
BlockingQueue方法
Java BlockingQueue接口有4组不同的方法,用于插入、删除和检查队列中的元素。当请求的操作不能及时执行时,每一组方法的操作都不同。下面是一个方法表:
这4种不同的行为意味着:
- Throws Exception:如果尝试的操作不能立即执行,则抛出异常。
- Special Value: 如果尝试的操作不能立即执行,则返回一个特殊值(通常为true / false)。
- Blocks: 如果尝试的操作不能立即执行,方法调用将阻塞,直到它能够执行为止。
- Times Out: 如果尝试的操作不能立即执行,方法调用会阻塞,但是等待的时间不会超过给定的超时时间。返回一个特殊值,告知操作是否成功(通常为true / false)。
不可能将null值插入到BlockingQueue中,如果您试图插入null, BlockingQueue将抛出一个NullPointerException。
你可以访问BlockingQueue中的所有元素,而不仅仅是开始和结束的元素。假设您已经将一个对象排队等待处理,但是你的程序决定取消它。然后你可以调用remove(o)来移除队列中的特定对象。然而,这并不是很有效,所以除非必要,否则不应该使用这些Collection方法。
Java BlockingQueue例子
下面是一个Java BlockingQueue示例。这个例子使用了BlockingQueue接口的ArrayBlockingQueue实现。
首先是BlockingQueueExample类,它在单独的线程中启动Producer和Consumer。生产者将字符串插入到共享的BlockingQueue中,而消费者将它们取出。
public class BlockingQueueExample {
public static void main(String[] args) throws Exception {
BlockingQueue queue = new ArrayBlockingQueue(1024);
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
new Thread(producer).start();
new Thread(consumer).start();
Thread.sleep(4000);
}
}
接下来是Producer类。注意它是如何在每次put()调用之间休眠一秒钟的。这将导致Consumer在等待队列中的对象时阻塞。
public class Producer implements Runnable{
protected BlockingQueue queue = null;
public Producer(BlockingQueue queue) {
this.queue = queue;
}
public void run() {
try {
queue.put("1");
Thread.sleep(1000);
queue.put("2");
Thread.sleep(1000);
queue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这是Consumer类。它只是从队列中取出对象,并将它们打印到System.out。
public class Consumer implements Runnable{
protected BlockingQueue queue = null;
public Consumer(BlockingQueue queue) {
this.queue = queue;
}
public void run() {
try {
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
add()
如果BlockingQueue内部还有多余的空间,add()方法将添加以方法参数形式传递的元素。如果BlockingQueue内部没有用于此新元素的空间,add()方法抛出一个IllegalStateException。
offer()
如果BlockingQueue内部还有多余的空间,offer()方法将添加以方法参数形式传递的元素。如果BlockingQueue内部没有用于此新元素的空间,offer()方法返回false。
offer(long millis, TimeUnit timeUnit)
offer()方法存在于一个以超时为参数的版本中。如果BlockingQueue内部有空间容纳它,或者空间变得可用,这个版本的offer()方法添加以方法参数形式传递的元素。如果BlockingQueue在超时时间内没有为这个新元素获得内部空间,这个版本的offer()方法返回false。
put()
如果BlockingQueue内部还有多余的空间, put()方法将添加元素到队列中。如果BlockingQueue没有空间容纳新元素,在BlockingQueue有新元素的内部空间之前,put()方法将阻塞调用put()方法的线程。
take()
take()方法将删除BlockingQueue中的第一个元素。如果BlockingQueue不包含任何元素,在一个元素被插入到BlockingQueue之前,take()方法将阻塞调用take()的线程。
poll()
poll()方法将删除BlockingQueue中的第一个元素。如果BlockingQueue不包含任何元素,那么poll()方法将返回null。
poll(long timeMillis, TimeUnit timeUnit)
BlockingQueue poll(long timeMillis, TimeUnit TimeUnit)方法将删除BlockingQueue中的第一个元素。如果BlockingQueue不包含任何元素,这个版本的poll()方法将等待给定的时间内的可用元素,作为参数传递给它。如果在给定的超时时间内没有元素可用,该方法将返回null。
remove(Object o)
如果元素存在于BlockingQueue中,remove(Object o)方法将从BlockingQueue 中删除给定元素的单个实例。remove()方法将使用o.equals(元素)来决定如果对象o作为参数传递BlockingQueue匹配一个给定的元素。如果BlockingQueue包含多个匹配给定参数o的元素,这些元素中只有一个将从BlockingQueue中删除。如果元素被删除,remove()方法将返回true,否则返回false。
peek()
peek()方法将返回BlockingQueue的第一个元素,而不删除它。如果BlockingQueue不包含任何元素,那么peek()方法将返回null。
element()
element()方法将返回BlockingQueue的第一个元素,而不删除它。如果BlockingQueue不包含任何元素,那么element()方法将抛出NoSuchElementException。
contains(Object o)
如果BlockingQueue包含一个与作为参数传递给contains() 方法的对象匹配的对象,contains(Object o)方法将返回true。用Objects.equals(o,element)的值去校验参数对象o是否匹配BlockingQueue中的给定元素。如果找到与参数对象匹配的元素,该方法返回true。如果没有找到匹配的元素,则返回false。
drainTo(Collection dest)
drainTo(Collection dest)方法将BlockingQueue的所有元素排入给定的目标集合中。
drainTo(Collection dest, int maxElements)
drainTo(Collection dest, int maxElements)从BlockingQueue中抽取maxElements个数的元素到给定的目标集合中。
size()
size()方法返回存储在BlockingQueue中的元素数量。
remainingCapacity
remainingCapacity()方法返回BlockingQueue剩余的(未使用的)容量。剩余容量计算方法为满容量减去BlockingQueue中存储的元素数量。