使用阻塞队列实现生产者 - 消费者模型
- 生产者:生产者线程负责生成任务并将其放入阻塞队列中。通过调用阻塞队列的
put
方法,当队列已满时,put
方法会阻塞当前线程,直到队列有可用空间。示例代码如下:
import java.util.concurrent.BlockingQueue;
public class Producer implements Runnable {
private final BlockingQueue<Runnable> queue;
public Producer(BlockingQueue<Runnable> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
Runnable task = generateTask();
try {
queue.put(task);
System.out.println("Produced: " + task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private Runnable generateTask() {
// 实际生成任务的逻辑
return () -> System.out.println("Task executed");
}
}
- 消费者:消费者线程从阻塞队列中取出任务并执行。通过调用阻塞队列的
take
方法,当队列已空时,take
方法会阻塞当前线程,直到队列中有任务可用。示例代码如下:
import java.util.concurrent.BlockingQueue;
public class Consumer implements Runnable {
private final BlockingQueue<Runnable> queue;
public Consumer(BlockingQueue<Runnable> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
Runnable task = queue.take();
System.out.println("Consumed: " + task);
task.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
- 启动线程:在主线程中创建阻塞队列实例,并启动生产者和消费者线程。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class Main {
public static void main(String[] args) {
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(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();
}
}
}
处理队列满和队列空的情况
- 队列满:使用
put
方法时,如果队列已满,当前线程会被阻塞,直到队列有空间可用。这避免了生产者在队列满时丢失任务或覆盖现有任务的问题。
- 队列空:使用
take
方法时,如果队列已空,当前线程会被阻塞,直到队列中有任务可用。这确保了消费者不会尝试处理空任务。
阻塞队列在多线程环境下相比于普通队列的优势
- 线程安全:阻塞队列提供了内置的线程安全机制,无需额外的同步操作。在多线程环境下,普通队列需要手动同步,容易出现死锁、竞态条件等问题。
- 阻塞操作:阻塞队列的
put
和take
方法可以在队列满或空时阻塞线程,这使得生产者 - 消费者模型的实现更加简洁和可靠。普通队列没有这种阻塞机制,需要手动实现等待和唤醒逻辑。
- 流量控制:阻塞队列可以通过设置容量来控制任务的堆积,避免系统因任务过多而耗尽资源。普通队列没有这种自动的流量控制能力。