多线程经典示例——生产者消费者模型
所谓的生产者消费者模型,是通过一个容器来解决生产者和消费者的强耦合问题。通俗的讲,就是生产者在不断的生产,消费者也在不断的消费,可是消费者消费的产品是生产者生产的,这就必然存在一个中间容器,我们可以把这个容器想象成是一个货架,当货架空的时候,生产者要生产产品,此时消费者在等待生产者往货架上生产产品,而当货架满的时候,消费者可以从货架上拿走商品,生产者此时等待货架的空位,这样不断的循环。那么在这个过程中,生产者和消费者是不直接接触的,所谓的‘货架’其实就是一个阻塞队列,生产者生产的产品不直接给消费者消费,而是仍给阻塞队列,这个阻塞队列就是来解决生产者消费者的强耦合的。就是生产者消费者模型。
– 生产者(生产数据)如:擀饺子皮
– 消费者(消费数据)如:包饺子
那么这么模型的作用是什么呢?
系统解耦,将系统模块化,每个角色负责自己的功能(比如现在很多公司分为前端和后端,前端负责页面处理,后端负责逻辑处理)
生产者和消费者都需要一个中间介质——阻塞队列
那么阻塞队列有哪些特征呢?
- 当生产者生产的比较快的收,队列已经放满了,生产者就会阻塞等待消费者消息之后来唤醒消费者继续生产
- 当消费者消费的比较快的时候,队列里面没有数据了,他就可以阻塞等待生产者生产了数据之后唤醒它继续消费
案例实现
实现一个队列,队列的实现方式有两种,链表和数组都可以,那么这里就用数组来实现,其中实现两个方法,一个put()添加元素操作,和pop()弹出队顶元素操作
put()操作 —— 生产者
pop()操作——消费者
1.当生产者put()操作生产的太快的时候,就让其等待,等到消费者pop()弹出元素空出空间来之后唤醒put()操作
2.当消费者pop()操作太快的时候,就让其等待,等到生产者生产出一条数据的时候,唤醒put()操作
注意wait()方法的使用
1.wait()是Object里面的方法,而不是Thread里面的,这一点很容易搞错。它的作用是将当前线程置于预执行队列,并在wait()所在的代码处停止,等待唤醒通知。
2.wait()只能在同步代码块或者同步方法中执行,如果调用wait()方法,而没有持有适当的锁,就会抛出异常。wait()方法调用后会释放出锁,线程与其他线程竞争重新获取锁。
notify()方法的使用
1.notify()方法也是要在同步代码块或者同步方法中调用的,它的作用是使停止的线程继续执行,调用notify()方法后,会通知那些等待当前线程对象锁的线程,并使它们重新获取该线程的对象锁,如果等待线程比较多的时候,则有线程规划器随机挑选出一个呈wait状态的线程。
2.notify()调用之后不会立即释放锁,而是当执行notify()的线程执行完成,即退出同步代码块或同步方法时,才会释放对象锁。
代码实现
import java.util.concurrent.TimeUnit;
//使用循环数组实现生产者消费者模型
public class ThreadDemo18 {
//Java里面的数组需要初始化容量,因为目前不知道,使用根据用户输入来决定
int[] items = null;
int first;//队首元素下标——出列的时候会用到
int last;//队尾元素下标——入队的时候会用到
int size;//实际存储容量
//初始化,传一个容量参数
public