阻塞队列(Blocking Queue)是 Java 并发编程中的一种数据结构,用于在多线程环境下安全地传递数据。它提供线程安全的插入和移除操作,并在需要时自动阻塞线程以避免竞争条件。阻塞队列通常用于实现生产者-消费者模式。
阻塞队列的种类
Java 提供了多种阻塞队列,主要包括:
- ArrayBlockingQueue:基于数组的有界阻塞队列。
- LinkedBlockingQueue:基于链表的可选有界阻塞队列。
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
- DelayQueue:支持延迟获取元素的无界阻塞队列。
- SynchronousQueue:每个插入操作必须等待相应的移除操作,反之亦然。
- LinkedTransferQueue:基于链表的无界阻塞队列,支持传输操作。
阻塞队列的方法
阻塞队列提供了多种方法,用于在不同的条件下插入和移除元素:
-
插入元素:
put(E e)
:如果队列满,则等待空间变得可用。offer(E e)
:尝试插入元素,如果队列满则立即返回false
。offer(E e, long timeout, TimeUnit unit)
:尝试在指定的时间内插入元素,如果队列满则等待。
-
移除元素:
take()
:如果队列为空,则等待元素变得可用。poll()
:尝试移除元素,如果队列为空则立即返回null
。poll(long timeout, TimeUnit unit)
:尝试在指定的时间内移除元素,如果队列为空则等待。
示例:使用 ArrayBlockingQueue
实现生产者-消费者模式
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ProducerConsumerExample {
private static final int QUEUE_CAPACITY = 10;
private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
public static void main(String[] args) {
Thread producerThread = new Thread(new Producer());
Thread consumerThread = new Thread(new Consumer());
producerThread.start();
consumerThread.start();
}
static class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("Producing " + i);
queue.put(i);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
static class Consumer implements Runnable {
@Override
public void run() {
while (true) {
try {
Integer value = queue.take();
System.out.println("Consuming " + value);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
}
各种阻塞队列的详细介绍
1. ArrayBlockingQueue
ArrayBlockingQueue
是一个基于数组的有界阻塞队列,必须在创建时指定其容量。
BlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<>(10);
2. LinkedBlockingQueue
LinkedBlockingQueue
是一个基于链表的阻塞队列,默认情况下是无界的,但可以在创建时指定其最大容量。
BlockingQueue<Integer> linkedBlockingQueue = new LinkedBlockingQueue<>(10);
3. PriorityBlockingQueue
PriorityBlockingQueue
是一个支持优先级排序的无界阻塞队列,元素按照自然顺序或者通过提供的 Comparator
进行排序。
BlockingQueue<Integer> priorityBlockingQueue = new PriorityBlockingQueue<>();
4. DelayQueue
DelayQueue
是一个支持延迟获取元素的无界阻塞队列,元素必须实现 Delayed
接口,元素只有在其延迟时间到期时才能从队列中取走。
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.DelayQueue;
class DelayElement implements Delayed {
private final long delayTime;
private final long expire;
public DelayElement(long delay, TimeUnit unit) {
this.delayTime = delay;
this.expire = System.currentTimeMillis() + unit.toMillis(delay);
}
@Override
public long getDelay(TimeUnit unit) {
long diff = expire - System.currentTimeMillis();
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return Long.compare(this.expire, ((DelayElement) o).expire);
}
@Override
public String toString() {
return "DelayElement{" + "expire=" + expire + '}';
}
}
public class DelayQueueExample {
public static void main(String[] args) {
DelayQueue<DelayElement> delayQueue = new DelayQueue<>();
delayQueue.put(new DelayElement(5, TimeUnit.SECONDS));
delayQueue.put(new DelayElement(10, TimeUnit.SECONDS));
while (true) {
try {
DelayElement element = delayQueue.take();
System.out.println("Taken: " + element);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
5. SynchronousQueue
SynchronousQueue
是一个不存储元素的阻塞队列,每一个 put
操作必须等待一个 take
操作,反之亦然。
BlockingQueue<Integer> synchronousQueue = new SynchronousQueue<>();
6. LinkedTransferQueue
LinkedTransferQueue
是一个基于链表的无界阻塞队列,支持 tryTransfer
和 transfer
操作,可以在消费者准备好之前传输元素。
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
public class LinkedTransferQueueExample {
private static final TransferQueue<Integer> transferQueue = new LinkedTransferQueue<>();
public static void main(String[] args) {
Thread producerThread = new Thread(new Producer());
Thread consumerThread = new Thread(new Consumer());
producerThread.start();
consumerThread.start();
}
static class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("Producing " + i);
transferQueue.transfer(i);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
static class Consumer implements Runnable {
@Override
public void run() {
while (true) {
try {
Integer value = transferQueue.take();
System.out.println("Consuming " + value);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
}
总结
阻塞队列在 Java 并发编程中是非常有用的工具,适用于各种场景。通过使用阻塞队列,可以简化多线程间的任务调度、资源共享和负载均衡,确保线程安全和高效的并发处理。
- 选择合适的阻塞队列:根据具体需求选择合适的阻塞队列,如有界队列(ArrayBlockingQueue)、无界队列(LinkedBlockingQueue)、优先级队列(PriorityBlockingQueue)、延迟队列(DelayQueue)等。
- 使用阻塞方法:使用
put
、take
等阻塞方法来确保生产者和消费者在适当的时机进行等待和唤醒,避免忙等待。 - 处理中断:在多线程环境中,正确处理中断异常,确保线程在中断时能正确响应并释放资源。
通过合理使用阻塞队列,可以编写高效且健壮的并发程序。