JAVA多线程——线程协作(生产者消费者问题)

tech2025-07-13  2

JAVA多线程——线程协作

生产者消费者问题

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

对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又要放到缓存区,通知消费者来消费

对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费

在生产者消费者问题中,仅有的synchronized是不够的

synchronized实现了同步,但不能用来实现不同线程之间的通信


解决线程之间通信问题的方法

wait() 让线程等待,直到其他线程通知,与sleep不同,会释放锁

wait(long timeout) 指定等待的毫秒数

notify() 唤醒处于等待状态的线程

notifyAll() 唤醒同一个对象上所有等待的线程,优先级别高的先调度

说明 这些方法都是定义在java.lang.Object类中的方法,都只能在同步方法或同步块中使用,方法的调用者必须是同步代码块或者同步方法中的同步监视器,否则会出现异常IllegalMonitorStateException

补充:sleep()和wait()的异同

同:一旦执行方法,都可以使当前线程进入阻塞状态

异:

两个方法声明的位置不同,Thread类中声明sleep(),Object类中声明wait()

结束阻塞状态的不同,sleep()时间到了就会结束,wait()要通过notifyAll() 方法

调用的要求不同,sleep()可以在任何场景下调用,wait()必须使用在同步代码块或者同步方法中

如果两个方法否使用在同步代码块或者同步方法中,sleep()不会释放锁,wait()会释放锁


​ 小结

​ 释放锁的操作:

当前线程的同步代码块、同步方法结束当前线程在同步代码块、同步方法结中遇到break、return终止了该代码块,该方法的继续执行当前线程在同步代码块、同步方法结中出现了未处理的Error或Execption,导致异常结束使用了wait()方法

不会释放锁的操作:

执行sleep(),和yield()方法

线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(尽量避免使用suspend()和resume()来控制线程)


解决方式

生产者消费者问题解决方式1:管程法

生产者:负责生产数据的模块(可能是放啊,对象,线程,进程)

消费者:负责处理数据的模块(可能是放啊,对象,线程,进程)

缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区

package com.peng.demon06; //测试:用管程法解决生产者消费者问题,利用缓冲区 //需要的对象:生产者,消费者,产品,缓冲区 public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); /*创建线程 Productor productor = new Productor(container); Consumer consumer = new Consumer( new Consumer); 开启线程 productor.start(); consumer.start(); 可以简化为下面两行代码 */ new Productor(container).start(); new Consumer(container).start(); } } //生产者 class Productor extends Thread{ SynContainer container; //用构造器创建容器的对象 public Productor(SynContainer container){ this.container=container; } //生产的方法 @Override public void run() { for (int i = 0; i < 100; i++) { container.push(new Chicken(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 Chicken{ int id;//定义产品的编号 public Chicken(int id) { this.id = id; } } //缓冲区(容器) class SynContainer{ //容器的大小,可以放10个产品 Chicken[] chickens = new Chicken[10]; //计算容器里产品的数量 int count = 0; //生产者放入产品 //synchronized同步方法,让线程同步 public synchronized void push(Chicken chicken){ //如果容器满了,就需要消费者消费,然后生产者等待 while (count == 10){ //生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果没有满,生产者丢入产品 chickens[count]=chicken;//把chicken丢进chickens数组的计数器里 count++; //放完通知消费者来消费 this.notifyAll(); } //消费者消费产品 public synchronized Chicken pop(){ //判断能否消费 while (count==0){ //如果容器为空, //等待生产者生产产品,消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消费 count--; Chicken chicken = chickens[count];//把chickens数组的计数器里的产品取出来 //消费完了,通知生产者生产,再放到缓冲区 this.notifyAll(); return chicken; } }

生产者消费者问题解决方式2:信号灯法

通过标志位来解决

package com.peng.demon06; //测试信号灯法——通过标志位 public class TestPC2 { public static void main(String[] args) { TV tv = new TV(); new Player(tv).start(); new Watcher(tv).start(); } } //生产者-->演员 class Player extends Thread{ TV tv = new TV(); public Player(TV tv){ this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { if (i%2==0){ tv.play("中餐厅"); }else { tv.play("广告"); } } } } //消费者-->观众 class Watcher extends Thread { TV tv = new TV(); public Watcher(TV tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { tv.watch(); } } } //产品-->节目(不需要缓冲区了) class TV{ //演员表演,观众等待 true //观众观看,演员等待 false String voice;//表演的节目 boolean flag = true;//标志位 //表演 public synchronized void play(String voice) { while (flag == false) {//如果标志位为false try { this.wait();//让演员等待 } catch (InterruptedException e) { e.printStackTrace(); } } //通知观众观看 this.notifyAll();//唤醒观众 this.voice = voice; System.out.println("演员表演了" + voice); this.flag = !this.flag;//取反 } //观看 public synchronized void watch(){ while (flag==true) {//如果标志位为true try { this.wait();//让观众等待 } catch (InterruptedException e) { e.printStackTrace(); } } //通知演员表演,唤醒等待 this.notifyAll(); System.out.println("观众观看了:" + voice); this.flag = !this.flag;//取反 } }

最新回复(0)