一.什么是阻塞式队
阻塞式队列(Blocking Queue)是一种特殊的队列数据结构,主要用于多线程并发场景,其核心特点是当队列的状态不满足操作条件时,相关线程会被阻塞,直到条件满足为止。以下是其关键特性和应用场景:
1.特点
- 线程安全:阻塞式队列内部实现了同步机制,所以多个线程能同时对它进行操作而不会出现数据不一致的问题。
- 阻塞操作:
- 当队列满时,尝试向队列添加元素的线程会被阻塞,直到队列有空间。
- 当队列为空时,尝试从队列获取元素的线程会被阻塞,直到队列中有元素
2.常用实现类
ArrayBlockingQueue
:基于数组实现的有界阻塞式队列。创建时需要指定容量,一旦达到容量上限,插入操作就会被阻塞。LinkedBlockingQueue
:基于链表实现的阻塞式队列,它可以是有界的,也可以是无界的(默认是无界的,最大容量为Integer.MAX_VALUE
)。SynchronousQueue
:没有容量的阻塞式队列,每个插入操作必须等待另一个线程的移除操作,反之亦然。PriorityBlockingQueue
:支持优先级排序的无界阻塞式队列,元素会根据自然顺序或指定的比较器进行排序。
二.生产者消费者模型
阻塞式队列常见的应用场景。是一种在多线程编程和并发系统中广泛使用的设计模式,它用于解决生产者和消费者之间的速度不匹配问题,实现数据的高效、安全传递。
- 生产者:负责生产数据或任务,并将其放入一个共享的缓冲区(如队列)中。
- 消费者:从共享缓冲区中取出数据或任务并进行处理。
- 缓冲区:作为生产者和消费者之间的中间存储区域,起到解耦和平衡生产者与消费者速度的作用。
三.代码示例
通过
ArrayBlockingQueue
实现了一个简单的生产者 - 消费者模型。生产者线程不断生成整数并放入队列,消费者线程从队列中取出整数进行处理。ArrayBlockingQueue
保证了线程安全,并且在队列满或空时会自动阻塞相应的操作
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class Producer implements Runnable {
private final BlockingQueue<Integer> queue;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
System.out.println("Producing: " + i);
queue.put(i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class Consumer implements Runnable {
private final BlockingQueue<Integer> queue;
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
Integer item = queue.take();
System.out.println("Consuming: " + item);
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
Thread producerThread = new Thread(new Producer(queue));
Thread consumerThread = new Thread(new Consumer(queue));
producerThread.start();
consumerThread.start();
try {
producerThread.join();
consumerThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
1.代码解释
1.生产者类 Producer
/1.Producer 类实现了 Runnable 接口,代表一个生产者线程任务
class Producer implements Runnable {
private final BlockingQueue<Integer> queue;
/2.构造函数接收一个 BlockingQueue 对象,用于存放生产的数据
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
/3.run 方法中,生产者生产 5 个整数,使用 put 方法将其放入队列,若队列满则阻塞。
每次生产后线程 休眠 100 毫秒。
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
System.out.println("Producing: " + i);
queue.put(i);
Thread.sleep(100);
}
/捕获 InterruptedException 异常,重新设置中断标志
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
2.消费者类 Consumer
/1.Consumer 类同样实现了 Runnable 接口,代表一个消费者线程任务
class Consumer implements Runnable {
private final BlockingQueue<Integer> queue;
/2.构造函数接收一个 BlockingQueue 对象,用于取出数据
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
/3.run 方法中,消费者从队列中取出 5 个整数,使用 take 方法,若队列为空则阻塞。
每次消费后线程休眠 200 毫秒
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
Integer item = queue.take();
System.out.println("Consuming: " + item);
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
3.主类 BlockingQueueExample
public class BlockingQueueExample {
public static void main(String[] args) {
/1.创建一个容量为 10 的 阻塞式队列(ArrayBlockingQueue 实例)
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
/2.创建生产者和消费者线程,并启动它们
Thread producerThread = new Thread(new Producer(queue));
Thread consumerThread = new Thread(new Consumer(queue));
producerThread.start();
consumerThread.start();
/3.使用 join 方法等待生产者和消费者线程执行完毕,
捕获 InterruptedException 异常并重新设置中断标志。
try {
producerThread.join();
consumerThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
总结
此代码利用 ArrayBlockingQueue
的阻塞特性,实现了一个简单且线程安全的生产者 - 消费者模型。通过 put
和 take
方法,自动处理队列满和空的情况,避免了复杂的线程同步操作。