700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【多线程并发编程】十一 生产者和消费者问题(面试必问)

【多线程并发编程】十一 生产者和消费者问题(面试必问)

时间:2021-10-18 14:40:40

相关推荐

【多线程并发编程】十一 生产者和消费者问题(面试必问)

程序猿学社的GitHub,欢迎Star

/ITfqyd/cxyxs

本文已记录到github,形成对应专题。

文章目录

前言1.概念2.模拟真实业务场景代码实战通过synchronized+wait+notifyall实现lock+await+signalAll

前言

生产者和消费者问题,是面试过程中,经常问到的一个问题,本文,就来了解一下,生产者和消费者是怎么一回事。

1.概念

生产者消费者问题是一个著名的线程同步问题,也是我们面试过程中,经常会问到的一个经典面试题,生产者负责生产数据放到缓冲区,消费者负责从缓存中里面取。

注意这个缓冲区的大小是有上限的。生产者发现供过于求,也就是说生产的速度太快了,也就是说,缓冲区里的数据,远远高于消费者的消费的能力,如果我们不做处理,就会造成速度的堆积,所以,该缓冲区是有上限的。可以理解为一个仓库,只能存放一定数据的产品,仓库都堆放满了,已经无地可放产品了,只能等消费者购买产品后,我们才能继续生产产品。生产者发现供不应求,说明消费者消费的速度远远高于生产者的速度,这时候,我们就只能耐心的等待。应该避免库里已经没有产品了还在消费。

提到生产者和消费者,顿时,我们的脑海里面会浮现不少的产品,rabbitmq,kafka,dubbo等等。

2.模拟真实业务场景

深圳某充电桩厂家,有一个小库房,能存放10台充电桩,为了保证不必要的浪费,利益最大化,要求库房满了,就停止生产,也不能造成库存的数据远远小于消费的数量。

代码实战

通过synchronized+wait+notifyall实现

package com.cxyxs.thread.eleven;/*** Description:* 转发请注明来源 程序猿学社 - https://ithub./* Author: 程序猿学社* Date: /2/29 10:25* Modified By:*/public class Pile {//库存数量private int sum=0;private int max=10;/*** 生产者*/public synchronized void pro(){while (sum == max){//达到最大值说明库存已经满了,无法存放充电桩try {this.wait(); //需要等待消费者,消费后,才能生存} catch (InterruptedException e) {e.printStackTrace();}}sum++;System.out.println(Thread.currentThread().getName()+":剩余充电桩数量为"+sum);this.notifyAll(); //通知消费者,我们库存有充电桩,赶紧来消费}/*** 消费者*/public synchronized void consumer(){while (sum == 0){try {this.wait(); //说明库房,充电桩不够了,需要等待充电桩厂家,继续生产充电桩} catch (InterruptedException e) {e.printStackTrace();}}sum --;System.out.println(Thread.currentThread().getName()+":剩余充电桩数量为"+sum);this.notifyAll(); //通知生产厂家,我已经消费了,你可以继续生产充电桩}}

测试类

package com.cxyxs.thread.eleven;/*** Description:synchronized+wait+notifyall解决生产者和消费者问题* 转发请注明来源 程序猿学社 - https://ithub./* Author: 程序猿学社* Date: /2/29 10:45* Modified By:*/public class Demo1{public static void main(String[] args) {Pile pile = new Pile();for (int j = 0; j < 3; j++) {new Thread(()->{for (int i = 0; i < 15; i++) {pile.pro();}},"生产者"+j).start();new Thread(()->{for (int i = 0; i < 15; i++) {pile.consumer();}},"消费者"+j).start();}}}

通过测试结果,我们可以看出,不会存在生产超出库存最大的情况,也不会出现没有产品后还在消费的问题。

重点,注意判断是否进入等待的时候,使用while,而不是if。如果使用if,可能会存在虚假唤醒的问题,所以这里,使用while,再次判断,避免虚假唤醒的问题。

lock+await+signalAll

java1.5版本以后,引入新的三剑客,一代新人换旧人。我们来看一看1.5版本的三剑客都有哪些。

lock ----------synchronizedwait ---------- awaitnotifyall ----- signal

synchronized可以理解为自动挡,会自动释放锁

Lock可以理解为手动挡,这种方式虽说灵活,但是需要自己手动释放锁,为了防止死锁,我们lock一般与try finally配套使用。

package com.cxyxs.thread.eleven;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*** Description:Lock方式* 转发请注明来源 程序猿学社 - https://ithub./* Author: 程序猿学社* Date: /2/29 18:16* Modified By:*/public class PileLock {//库存数量private int sum=0;private int max=10;final Lock lock = new ReentrantLock();private Condition pro= lock.newCondition();private Condition consumer= lock.newCondition();/*** 生产者*/public void pro(){try {lock.lock(); //上锁while (sum == max){//达到最大值说明库存已经满了,无法存放充电桩try {pro.await(); //需要等待消费者,消费后,才能生存} catch (InterruptedException e) {e.printStackTrace();}}sum++;System.out.println(Thread.currentThread().getName()+":剩余充电桩数量为"+sum);pro.signalAll(); //通知消费者,我们库存有充电桩,赶紧来消费}finally {lock.unlock(); //重点}}/*** 消费者*/public void consumer(){try {lock.lock(); //上锁while (sum == 0){try {consumer.await(); //说明库房,充电桩不够了,需要等待充电桩厂家,继续生产充电桩} catch (InterruptedException e) {e.printStackTrace();}}sum --;System.out.println(Thread.currentThread().getName()+":剩余充电桩数量为"+sum);consumer.signalAll(); //通知生产厂家,我已经消费了,你可以继续生产充电桩} finally {lock.unlock(); //重点}}}

测试类

package com.cxyxs.thread.eleven;/*** Description:synchronized+wait+notifyall解决生产者和消费者问题* 转发请注明来源 程序猿学社 - https://ithub./* Author: 程序猿学社* Date: /2/29 10:45* Modified By:*/public class Demo1{public static void main(String[] args) {// 通过synchronized+wait+notifyall实现// Pile pile = new Pile();//lock+await+signalPileLock pile = new PileLock();for (int j = 0; j < 3; j++) {new Thread(()->{for (int i = 0; i < 10; i++) {pile.pro();}},"生产者"+j).start();new Thread(()->{for (int i = 0; i < 10; i++) {pile.consumer();}},"消费者"+j).start();}}}

注意一定要用try finally,通过finally设置解锁,以确保在必要时释放锁定。我们使用Lock来产生两个Condition对象来管理任务间的通信,一个是生产者,一个是消费者。lock() 获取锁,unlock() 释放锁await() 使当前线程加入 等待队列中,并释放当锁.当其他线程调用signal()会重新请求锁signal会唤醒一个在 await()等待队列中的线程,signalAll会唤醒 await()等待队列中所有的线程

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。