生产者消费者模型丐中丐实现!!!
目录
引言
在面试中,多线程编程愈发常见,生产者-消费者模型是一种经典的线程通信模式,广泛应用于任务调度、消息处理、异步执行等场景。本文将通过一段实战代码示例,深入剖析如何使用 Java 的并发工具构建一个高并发安全、结构清晰的任务调度框架。
本文将围绕以下核心问题展开:
- 如何控制生产和消费的节奏?
- 如何保证任务数量不多不少、恰好执行完?
- 如何优雅处理线程终止问题?
- 如何进一步优化提升系统吞吐量?
代码结构总览
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class Main {
private static final int QUEUE_SIZE = 12;
private static final int NUM_PRODUCER = 10;
private static final int NUM_CONSUMER = 8;
private static final int NUM_TASK = 100;
private static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(QUEUE_SIZE);
private static AtomicInteger count1 = new AtomicInteger(0);
private static AtomicInteger count2 = new AtomicInteger(0);
public static void main(String[] args) {
Thread[] producers = new Thread[NUM_PRODUCER];
Thread[] consumers = new Thread[NUM_CONSUMER];
for (int i = 0; i < NUM_PRODUCER; i++) {
producers[i] = new Thread(new Producer(i));
producers[i].start();
}
for (int i = 0; i < NUM_CONSUMER; i++) {
consumers[i] = new Thread(new Consumer(i));
consumers[i].start();
}
try {
for (Thread p : producers) p.join();
for (Thread c : consumers) c.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ALL TASK HAS DONE.");
}
static class Producer implements Runnable {
final int id;
public Producer(int id) {
this.id = id;
}
public void run() {
while (true) {
if (count1.get() >= NUM_TASK) break;
try {
int curId = count1.incrementAndGet();
queue.put(curId);
System.out.println("P " + id + " HAS PRODUCED TASK " + curId);
Thread.sleep((int) (Math.random() * 2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Consumer implements Runnable {
final int id;
public Consumer(int id) {
this.id = id;
}
public void run() {
while (true) {
if (count2.get() >= NUM_TASK) break;
try {
Integer curId = queue.poll();
if (curId == null) {
Thread.sleep(10);
continue;
}
count2.incrementAndGet();
System.out.println("C " + id + " HAS CON SUMED TASK " + curId);
Thread.sleep((int) (Math.random() * 2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
这段代码实现了一个典型的生产者消费者模型:
- 队列容量: 12
- 生产者线程数: 10 个
- 消费者线程数: 8 个
- 总任务数: 100 个
关键成员包括:
BlockingQueue<Integer> queue:用于存储任务的共享队列;AtomicInteger count1, count2:分别用于统计已生产与已消费的任务数,保证线程安全;Producer和Consumer实现了Runnable接口,分别处理任务的生成与消费。
核心代码讲解
共享资源
private static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(QUEUE_SIZE);
private static AtomicInteger count1 = new AtomicInteger(0);
private static AtomicInteger count2 = new AtomicInteger(0);
使用 LinkedBlockingQueue 实现线程安全的任务传递;
AtomicInteger 替代 synchronized,保证原子性并避免锁竞争。
生产者逻辑
while (true) {
if (count1.get() >= NUM_TASK) break;
try {
int curId = count1.incrementAndGet();
queue.put(curId);
System.out.println("P " + id + " HAS PRODUCED TASK " + curId);
Thread.sleep((int) (Math.random() * 2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
每个生产者线程不断尝试生成任务,直到总任务数达到上限。
queue.put(curId) 为阻塞操作,当队列满时自动等待。
注意
使用 incrementAndGet() 而非 getAndIncrement() 是为了防止出现越界任务。
任务编号可能不连续(由于并发 + 条件判断的竞态),但总量可控。
消费者逻辑
while (true) {
if (count2.get() >= NUM_TASK) break;
try {
Integer curId = queue.poll();
if (curId == null) {
Thread.sleep(10);
continue;
}
count2.incrementAndGet();
System.out.println("C " + id + " HAS CONSUMED TASK " + curId);
Thread.sleep((int) (Math.random() * 2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
消费者采用 poll() 非阻塞拉取,空则等待一段时间重试,确保所有消费者线程不阻塞;
成功获取任务后,立即执行并统计。

浙公网安备 33010602011771号