写点什么

《重学 Java 高并发》手写生产者消费者模型

发布于: 1 小时前
《重学Java高并发》手写生产者消费者模型

《重学 Java 高并发》专栏特色:结合 10 余年的工作经验,在实践中提炼总结高并发经验,将理论落到实处,不仅助力面试,更是真正提高技能。

温馨提示:本文为该系列的第一篇。

java 多线程是每一个 java 程序员必备的技能,但多线程对于初学者来说学起来比较困难,结合笔者多年的学习经验,能手写一个简单的生产者消费者模型,是深入了解多线程编程的前提条件。

在 java 中,线程与线程之间基本存在两种关系:

  • 协作一个线程的驱动需要与另外一个线程合作,共同合作完成一件事情

  • 竞争多个线程互斥,即多个线程只能串行执行。

本文想来和大家说说多个线程的协作,这个非常有助于理解 notify、wait 方法。

1、消费者/生产者场景

一个非常经典的场景:面包厂生产面包。

在一个面包厂,面包的仓库容积有限,生产工人可以继续生产面包的条件是仓库还有足够的空间,生产的面包是需要派送工人卖给顾客,派送工人要能派送面包的条件是仓库中有剩余的面包。

大概的场景到交付如下图所示:


2、代码实现

有了场景,接下来我们使用 java 写一个简易的生产者、消费者。

本示例中涉及到类主要如下图所示:

其类的职责说明如下:

  • Bakery 面包厂仓库,主要用来存放面包。

  • BreadWork 面包生产工人

  • BreadConsume 面包消费工人

  • Bread 面包

接下来将和大家一一展示代码,同时在介绍代码时将重点阐述线程合作时的一些重点知识,并将提出一个更高难度的思考题供大家挑战。

2.1 Bakery 核心实现

Bakery 是整个生产者、消费者模型的核心实现类,与多线程编程相关的核心要点也体现在该方法中,其整体代码如下:

其核心要点解释如下:

  • Object bakeryLock 锁对象,主要是用来保护 List< Bread> 数据结构,众所周知,ArrayList 是多线程不安全的,也就是说多个线程对其进行访问,必须加锁,这里之所以单独创建一个对象,主要是想突出锁概念,在代码中,其实可以用 synchronized(breads) 来代替。

  • put 方法该方法是被面包生产者调用,向面包厂中添加面包,但是面包厂的容量是有限的,即生产者不能一直往里面添加,即当达到最大容量后,需要阻止生产者继续往里面添加,故这里涉及到条件等待与 wait 方法,细细说明如下:访问 breads 数据结构之前,先使用 synchronized 进行保护,即加锁。如果仓库已满,需要调用锁对象的 wait 方法,调用锁对象的线程,也就是生产者线程会被阻塞,需要等待其他线程的唤醒,唤醒后才能继续执行后续代码。如果仓库还有空间,则向仓库中添加一个面包,此时另外一个隐含的条件将满足:仓库中已经有面包了,而消费者可能会因为仓库中没有面包而阻塞,故这里需要调用锁对象的 notify 或 nofifyAll 方法,唤醒等待的消费者。

  • get 方法该方法主要是被面包消费者调用,从面包中获取面包,但要能获取面包也是有条件的:仓库中存在面包,否则需要阻塞等待消费者创建面包,故这里的要点如下:如果仓库中没有面包,调用锁对象的 wait 方法,则消费者线程将进入阻塞状态,其具体实现是消费者线程对象会放在锁对象的条件等待队列,将等待其他线程调用锁的 notify 或 notifyAll。如果有面包,则从中消费一个面包,此时另外一个隐含的条件将满足:仓库中已经有新的空间存放新的面包,故此时应该调用锁对象的 notify 或 notifyAll,唤醒生产者。

请大家细品:notify,wait 方法是调用的锁对象,并不是生产者或消费者线程。 欢迎大家私信我,交流心得体会。

2.2 生产者/消费者代码实现

在该示例中生产者、消费者创建面包,并尝试存储在面包厂中,其代码示例如下:

消费者、生产者代码比较简单,就不做过多说明。

温馨提示:如果需要整套代码,可以私信我,回复 TCODE 即可获得。

2.3 运行效果与进阶

该示例的运行效果如下图所示:

上面的示例其实只是一个入门,重点是了解锁对象,线程之间如何通过 notify、wait 方法进行协同“作战”,有了上面的示例,我想将难度系数再次提高:

如果做到生产者、消费者交替运行,即生产者生产面包 1 号,需要等待消费者消费完面包 1 号后,生产者才能继续生产 2 号面包。


文章首发于公众号「中间件兴趣圈」https://mp.weixin.qq.com/s/c7mpY2z6AY9w9zf8cehxqA


作者简介:丁威,《RocketMQ 技术内幕》一书作者、RocketMQ 开源社区优秀布道师,公众号「中间件兴趣圈」维护者,主打成体系剖析 Java 主流中间件,已发布 Kafka、RocketMQ、Dubbo、Sentinel、Canal、ElasticJob 等中间件 15 个专栏。

发布于: 1 小时前阅读数: 6
用户头像

『中间件兴趣圈』《RocketMQ技术内幕》 2020.11.30 加入

《RocketMQ技术内幕》作者、RocketMQ社区优秀布道师、中通科技技术平台部资深架构师、专注于JAVA中间件领域的源码分析、原理与实战。

评论

发布
暂无评论
《重学Java高并发》手写生产者消费者模型