- 生产者/消费者问题也称有限缓冲问题,是一个多线程同步的问题
- 生产者/消费者问题描述了两个共享共同大小缓冲区的线程之间如何处理生产数据和处理数据的关系的问题
管程法
-
管程法是解决生产者/消费者问题的一种方法,该方法中主要包含三部分:
- 生产者:负责生产数据的模块(可能是方法、对象、线程、进程)
- 消费者:负责处理数据的模块(可能是方法、对象、线程、进程)
- 缓冲区:用于存放数据的地方,可以被生产者和消费者操作
-
执行过程:生产者将数据放入缓冲区,消费者将数据从缓冲区拿出
-
代码案例:
// 生产者 class Producer implements Runnable { private Table table; public Producer(Table table) { this.table = table; } @Override public void run() { for (int i = 0; i < 100; i++) { try { this.produce(i+1); // 制作面包 } catch (InterruptedException e) { e.printStackTrace(); } } } // 制作面包 public void produce(int i) throws InterruptedException { synchronized (this.table) { if (this.table.getCount() >= Table.MAX) { this.table.wait(); // 等待消费者消费 } Bread bread = new Bread("制作的第" + i + "个面包"); // 制作一个新的面包 this.table.push(bread); // 将面包放到摊桌上 System.out.println("制作了第" + i + "个面包,当前面包数为"+this.table.getCount()); this.table.notifyAll(); // 通知消费者消费 } } } // 消费者 class Consumer implements Runnable { private Table table; public Consumer(Table table) { this.table = table; } @Override public void run() { for (int i = 0; i < 100; i++) { try { this.consume(); // 消费面包 } catch (InterruptedException e) { e.printStackTrace(); } } } // 消费面包 public void consume() throws InterruptedException { synchronized (this.table) { if (this.table.getCount() == 0) { this.table.wait(); // 等待生产者生产 } Bread bread = this.table.pop(); // 将面包从摊桌上拿出 System.out.println("正在吃"+bread.getName()); // 吃面包 this.table.notifyAll(); // 通知生产者生产 } } } // 产品:面包 class Bread { private String name; public String getName() { return this.name; } public Bread(String name) { this.name = name; } } // 缓冲区:摊桌 class Table { public static final int MAX = 10; // 最大面包数 private Bread[] breads = new Bread[MAX]; // 面包数组 private int count = 0; // 当前面包数 public int getCount() { return this.count; } // 放入面包 public void push(Bread bread) { this.breads[count++] = bread; } // 拿出面包 public Bread pop() { return this.breads[--count]; } } // 生产者/消费者问题解决1:管程法 public class ProducerAndConsumerDemo01 { public static void main(String[] args) { // 创建启动线程 Table table = new Table(); new Thread(new Producer(table)).start(); new Thread(new Consumer(table)).start(); } }
信号灯法
-
信号灯法是解决生产者/消费者问题的另一种方法,该方法中主要用一个”信号灯“(标志位)来通知生产者生产或消费者消费
-
执行过程:生产者先生产数据然后改变标志位,消费者看到标志位改变后拿走数据
-
代码案例:
// 生产者:视频制作UP class Producer2 implements Runnable { private Bilibili bilibili; public Producer2(Bilibili bilibili) { this.bilibili = bilibili; } @Override public void run() { for (int i = 0; i < 100; i++) { try { this.makeVideo(i+1); // 制作视频 } catch (InterruptedException e) { e.printStackTrace(); } } } // 制作视频 public void makeVideo(int i) throws InterruptedException { synchronized (this.bilibili) { while (this.bilibili.isFlag()) { System.out.println("再发个动态~~~"); this.bilibili.wait(); // 等待消费者观看 } this.bilibili.push("第"+i+"个视频"); // 传入一个视频 System.out.println("UP主制作了第"+i+"个视频,发个动态让粉丝观看~~~"); this.bilibili.notifyAll(); // 通知消费者观看 } } } // 消费者:观众 class Consumer2 implements Runnable { private Bilibili bilibili; public Consumer2(Bilibili bilibili) { this.bilibili = bilibili; } @Override public void run() { for (int i = 0; i < 100; i++) { try { this.watchVideo(); } catch (InterruptedException e) { e.printStackTrace(); } } } // 观看视频 public void watchVideo() throws InterruptedException { synchronized (this.bilibili) { while (!this.bilibili.isFlag()) { System.out.println("没有视频看,催更~~~"); this.bilibili.wait(); } System.out.println("粉丝观看UP主的"+this.bilibili.pop()); System.out.println("看完了,催更~~~"); this.bilibili.notifyAll(); // 通知生产者制作视频 } } } // 产品:Bilibili视频 class Bilibili { private String video; // 视频 private boolean flag = false; // 有无视频 public boolean isFlag() { return flag; } // 传入视频 public void push(String video) { this.change(); this.video = video; } // 传出视频 public String pop() { this.change(); return this.video; } // 改变标志符 private void change() { this.flag = !this.flag; } } // 生产者/消费者问题解决2:信号灯法 public class ProducerAndConsumerDemo02 { public static void main(String[] args) { // 创建启动线程 Bilibili bilibili = new Bilibili(); new Thread(new Producer2(bilibili)).start(); new Thread(new Consumer2(bilibili)).start(); } }