Java中的BlockingDeque
在Java中,BlockingDeque是双端队列(Deque)的一种特殊形式。它允许在队列的两端插入或删除元素,并且支持阻塞操作。简而言之,它就是一个能够在生产者和消费者之间传递元素的数据结构。本文将详细讨论BlockingDeque在Java中的使用方法和一些示例代码。
核心接口和方法
BlockingDeque提供了一些核心接口和方法,如下所示:
- addFirst(E e):在双端队列的头部添加一个元素,如队列满了,抛出IllegalStateException异常。
- addLast(E e):在双端队列的尾部添加一个元素,如队列满了,抛出IllegalStateException异常。
- offerFirst(E e):在双端队列的头部添加一个元素,如队列满了,直接返回false。
- offerLast(E e):在双端队列的尾部添加一个元素,如队列满了,直接返回false。
- putFirst(E e):在双端队列的头部添加一个元素,如队列满了,阻塞等待。
- putLast(E e):在双端队列的尾部添加一个元素,如队列满了,阻塞等待。
- takeFirst():从双端队列的头部取出一个元素,如队列为空,阻塞等待。
- takeLast():从双端队列的尾部取出一个元素,如队列为空,阻塞等待。
- pollFirst():从双端队列的头部取出一个元素,如队列为空,直接返回null。
- pollLast():从双端队列的尾部取出一个元素,如队列为空,直接返回null。
- removeFirst():从双端队列的头部删除一个元素,如队列为空,抛出NoSuchElementException异常。
- removeLast():从双端队列的尾部删除一个元素,如队列为空,抛出NoSuchElementException异常。
- peekFirst():查看双端队列的头部元素,如队列为空,直接返回null。
- peekLast():查看双端队列的尾部元素,如队列为空,直接返回null。
示例代码
让我们来看一些示例代码,以更好地理解BlockingDeque的使用方法。
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
public class Example {
public static void main(String[] args) throws Exception {
BlockingDeque<Integer> deque = new LinkedBlockingDeque<>(2);
deque.addFirst(1);
deque.addLast(2);
// deque.addLast(3);// 抛出IllegalStateException异常
deque.putFirst(0);
System.out.println(deque.takeLast());// 2
System.out.println(deque.takeFirst());// 0
System.out.println(deque.pollLast());// 1
System.out.println(deque.pollFirst());// null
}
}
上述示例代码创建了一个容量为2的LinkedBlockingDeque,然后在其头部和尾部添加元素1和2。接着,在容量满的情况下,使用putFirst()方法将元素0添加到队列中去,该方法会一直阻塞直到队列有空余位置。最后,我们依次使用takeLast()、takeFirst()、pollLast()和pollFirst()方法取出元素,其中,takeXXX()方法是阻塞等待,而pollXXX()方法是直接返回null或者队列中的元素。
另外,BlockingDeque可以被用作生产者-消费者模式的并发队列。下面是一个示例:
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
public class ProducerConsumer {
public static void main(String[] args) {
BlockingDeque<Integer> deque = new LinkedBlockingDeque<>(2);
new Thread(new Producer(deque)).start();
new Thread(new Consumer(deque)).start();
}
static class Producer implements Runnable {
private final BlockingDeque<Integer> deque;
Producer(BlockingDeque<Integer> deque) {
this.deque = deque;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
deque.put(i);
System.out.println(Thread.currentThread().getName() + " 生产 " + i + " 成功");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Consumer implements Runnable {
private final BlockingDeque<Integer> deque;
Consumer(BlockingDeque<Integer> deque) {
this.deque = deque;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Integer value = deque.take();
System.out.println(Thread.currentThread().getName() + " 消费 " + value + " 成功");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
上述示例代码创建了一个容量为2的LinkedBlockingDeque,并启动了一个生产者线程和一个消费者线程。在生产者线程中,循环向队列中添加元素,如果队列已满,则会阻塞等待。在消费者线程中,循环从队列中取出元素,如果队列为空,则会阻塞等待。
结论
BlockingDeque作为双端队列的一种特殊形式,具有阻塞的特性。它可以被用作生产者-消费者模式的并发队列,从而实现线程安全的数据交换。在实际应用中,我们可以根据业务场景选择不同的队列实现。当我们需要在队列的两端进行插入或删除操作,并且希望在队列已满或为空时进行阻塞等待时,BlockingDeque会是一个不错的选择。