Java中的LinkedBlockingDeque和示例
LinkedBlockingDeque是Java集合中的一个实现,它是一个线程安全的双向队列,支持高效的插入、删除和遍历操作。它是BlockingDeque接口的一个具体实现,其内部实现使用了链表数据结构。
LinkedBlockingDeque可以用于各种并发编程场景,例如作为生产者和消费者之间的缓存队列,或作为线程池中的工作队列等。
下面我们来看一下LinkedBlockingDeque的一些示例。
初始化LinkedBlockingDeque
创建LinkedBlockingDeque时,可以不指定大小,也可以指定一个容量。如果未指定容量,则默认容量为Integer.MAX_VALUE。
// 初始化一个无限容量的LinkedBlockingDeque
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
// 初始化一个容量为10的LinkedBlockingDeque
LinkedBlockingDeque<Integer> deque2 = new LinkedBlockingDeque<>(10);
入队和出队操作
入队操作用add(E e)
或offer(E e)
方法,出队操作用remove()
或poll()
方法。如果队列为空,remove()
和poll()
方法会返回null或抛出异常,而add()
和offer()
方法会返回false或抛出异常。
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
// 入队操作
deque.add(1); // 添加到队列的尾部
deque.offer(2); // 添加到队列的尾部
// 出队操作
int head = deque.poll(); // 获取队列头部元素并删除
如果想在队列头部或尾部插入元素,可以使用addFirst(E e)
、addLast(E e)
、offerFirst(E e)
、offerLast(E e)
等方法。
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
// 入队操作
deque.addFirst(1); // 添加到队列的头部
deque.addLast(2); // 添加到队列的尾部
// 出队操作
int head = deque.pollFirst(); // 获取队列头部元素并删除
获取队列元素
除了出队操作之外,还有一些方法可以获取队列中的元素,例如peek()
、peekFirst()
、peekLast()
方法可以获取队列头部或尾部的元素而不删除它们。
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
deque.add(1);
deque.add(2);
System.out.println(deque.peek()); // 输出:1
System.out.println(deque.peekFirst()); // 输出:1
System.out.println(deque.peekLast()); // 输出:2
另外,iterator()
方法可以用于迭代队列中的元素。
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
deque.add(1);
deque.add(2);
Iterator<Integer> iterator = deque.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
阻塞等待
LinkedBlockingDeque支持阻塞等待操作,当队列为空时,会阻塞调用方线程,直到队列中有元素可以出队或入队时再恢复线程。
take()
方法可以获取并删除队列头部元素,如果队列为空,则会一直阻塞线程。
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
// 新开一个线程向队列中添加元素
new Thread(() -> {
try {
deque.put(1);
Thread.sleep(1000);
deque.put(2);
Thread.sleep(1000);
deque.put(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 从队列中取出元素
System.out.println(deque.take()); // 阻塞等待,直到队列中有元素
System.out.println(deque.take()); // 阻塞等待,直到队列中有元素
System.out.println(deque.take()); // 阻塞等待,直到队列中有元素
put(E e)
方法用于将元素插入到队列尾部,如果队列已满,则会一直阻塞调用方线程,直到队列中有空闲位置可以插入元素。
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>(2);
// 新开一个线程向队列中添加元素
new Thread(() -> {
try {
deque.put(1);
deque.put(2); // 队列已满,会阻塞等待
deque.put(3); // 队列已满,会阻塞等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 从队列中取出元素
System.out.println(deque.take()); // 输出:1
System.out.println(deque.take()); // 输出:2
System.out.println(deque.take()); // 阻塞等待,直到队列中有元素
并发操作示例
下面是一个并发场景的示例,其中生产者向队列中添加元素,消费者从队列中取出元素并进行处理。
LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>(10);
// 生产者线程
Thread producer = new Thread(() -> {
for (int i = 1; i <= 100; i++) {
try {
deque.put(i); // 队列满时会阻塞等待
System.out.println("Produced: " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
while (true) {
try {
int num = deque.take(); // 队列空时会阻塞等待
System.out.println("Consumed: " + num);
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
consumer.start();
结论
Java中的LinkedBlockingDeque提供了一个线程安全的双向队列实现,支持高效的入队、出队和遍历操作,同时也支持阻塞等待的操作。它可以用于各种并发编程场景,例如生产者和消费者之间的缓存队列、线程池中的工作队列等。