生产者消费者模型设计
生产者消费者模型设计必须满足以下几点
- 生产者和消费者共享一个消息队列, 且所有线程在对共享队列进行操作的时候都必须加锁
- 生产者在队列满的时候不能再生产,必须阻塞自己
- 消费者在队列空的时候不能再消费,必须阻塞自己
- 共享队列从无到有的时候必须唤醒阻塞的消费者
- 共享队列从满到不满的时候必须唤醒阻塞的生产者
1. 首先建造一个产品类
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Product {
private String name;
}
2. 建造一个生产者类,为了实现多线程场景下的生产,需要实现Runnable接口, 重写run方法
import java.util.Queue;
import java.util.Random;
public class Producer implements Runnable{
private Queue<Product> queue;
private int maxCapacity;
public Producer(Queue queue, int maxCapacity) {
this.queue = queue;
this.maxCapacity = maxCapacity;
}
@Override
public void run() {
synchronized (queue) {
// 必须用while, 因为当线程唤醒之后, 有可能还处于队列满的状态, 此时不能继续向下执行, 必须再次进行判断
while (queue.size() == maxCapacity) {
try {
System.out.println("生产者" + Thread.currentThread().getName() + "等待中... Queue 已达到最大容量,无法生产");
queue.wait(); // wait 释放锁
System.out.println("生产者" + Thread.currentThread().getName() + "退出等待");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//队列里的产品从无到有,需要通知在等待的消费者
if (queue.size() == 0) queue.notifyAll();
Integer productId = new Random().nextInt();
queue.offer(new Product("产品" + productId));
System.out.println(Thread.currentThread().getName() + "--------生产了产品:" + productId);
}
}
}
3. 建造一个消费者类,为了实现多线程场景下的生产,需要实现Runnable接口, 重写run方法
import java.util.Queue;
public class Consumer implements Runnable{
private Queue<Product> queue;
private int maxCapacity;
public Consumer(Queue queue, int maxCapacity) {
this.queue = queue;
this.maxCapacity = maxCapacity;
}
@Override
public void run() {
synchronized (queue) {
// 必须用while, 因为当线程唤醒之后, 有可能还处于队列空的状态, 此时不能继续向下执行, 必须再次进行判断
while (queue.isEmpty()) {
try {
System.out.println(Thread.currentThread().getName() + "等待中... Queue 已缺货,无法消费");
queue.wait();
System.out.println(Thread.currentThread().getName() + "退出等待");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (queue.size() == maxCapacity) queue.notifyAll();
Product product = queue.poll();
System.out.println(Thread.currentThread().getName() + "--------消费了:" + product.getName());
}
}
}
4.模拟100个线程进行生产,100个线程进行消费,生产者和消费者共享一个队列,所以所有的线程在对队列进行操作的时候都必须加锁
import java.util.ArrayDeque;
import java.util.Queue;
public class Test {
public static void main(String[] args) {
// 生产者消费者共享的是同一个 queue
Queue<Product> queue = new ArrayDeque<>();
for (int i = 0; i < 100; i++) {
new Thread(new Producer(queue, 100), "生产者" + (i + 1)).start();
new Thread(new Consumer(queue, 100), "消费者" + (i + 1)).start();
}
}
}