kuangshenshuo-多线程-生产者消费者问题-管程法&信号灯法

it2025-06-08  17

多线程详解 -> 线程通信 ——生产者消费者问题

1.1 线程通信应用场景:生产者消费者问题

假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到长裤中再次放入产品为止

1.2 问题分析

这是一个线程同步问题,生产者和消费者共享一个资源,并且生产者和消费者之间相互依赖,互为条件

对于生产者,没有生产产品之前,要通知消费者等待;而生产了产品后,需要马上通知消费者消费对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费在生产者消费者问题中,仅有synchronized是不够的 synchronized可以阻止并发更新同一个共享资源,实现了同步 synchronized不能用来实现不同线程之间的消息传递,即通信

Java提供了几个方法解决线程之间的通信问题

均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateException 方法作用wait()表示线程一直等待,直到其他线程通知;与sleep()不同,会释放锁wait(long timeout)指定等待的毫秒数notify()唤醒一个处于等待状态的线程notifyAll()唤醒同一个对象上所有调用wait()的线程,优先级别高的线程优先调度

1.3 解决方式1 --> 管程法

生产者:负责生产数据的模块,可能是方法、对象、线程、进程;消费者:复制处理数据的模块,可能是方法、对象、线程、进程;缓冲区:消费者不能直接使用生产者的数据,它们之间有一个缓冲区;生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据 //测试生产者消费者问题 --> 利用缓冲区解决(管程法) //生产者、消费者、产品、缓冲区 public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Producer(container).start(); new Consumer(container).start(); } } //生产者 class Producer extends Thread{ SynContainer container; public Producer(SynContainer container){ this.container = container; } //生产 @Override public void run() { for (int i = 0; i < 100; i++) { container.push(new Product(i)); System.out.println("生产者:生产了第"+i+"个产品"); } } } //消费者 class Consumer extends Thread{ SynContainer container; public Consumer(SynContainer container){ this.container = container; } //消费 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("消费者:消费了第"+container.pop().id + "个产品"); } } } //产品 class Product{ int id; public Product(int id){ this.id = id; } } //缓冲区 class SynContainer{ //需要一个容器大小 Product[] products = new Product[10]; //容器计数器 int count = 0; //需要生产者放入产品 public synchronized void push(Product product){ //如果容器满了,就需要等待消费者消费 if(count == products.length){ //通知消费者消费,生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果没有满,就需要放入产品 products[count] = product; count ++; //通知消费者可以消费了 this.notifyAll(); } //需要消费者消费产品 public synchronized Product pop(){ //如果容器为空,无法消费,就需要等待等待生产者生产 if(count == 0){ //等待生产者生产,消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消费 count --; Product product = products[count]; //通知生产者生产 this.notifyAll(); return product; } } 会出现先消费后生产问题??

1.4 解决方式2 --> 信号灯法

//测试生产者消费者问题:信号灯法(利用标志位解决) //演员、观众、节目 public class TestPC2 { public static void main(String[] args) { Show show = new Show(); new Player(show).start(); new Audience(show).start(); } } //生产者:演员 class Player extends Thread{ Show show; public Player(Show show){ this.show = show; } @Override public void run() { for (int i = 0; i < 20; i++) { if(i%2 == 0){ this.show.play("快乐大本营"); }else{ this.show.play("抖音:记录美好生活"); } } } } //消费者:观众 class Audience extends Thread{ Show show; public Audience(Show show){ this.show = show; } @Override public void run() { for (int i = 0; i < 20; i++) { show.watch(); } } } //产品:节目 class Show{ //演员表演的时候,观众等待 T //观众观看的时候,演员等待 F String voice; boolean flag = true; //演员表演节目 public synchronized void play(String voice){ if(!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员表演了" + voice); //通知观众观看 this.notifyAll(); this.voice = voice; this.flag = !this.flag; } //观众观看节目 public synchronized void watch(){ if(flag == true){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("观众观看了" + voice); //通知演员表演 this.notifyAll(); this.flag = !this.flag; } }
最新回复(0)