《重学Java高并发》之“摸底考试”:你会使用多线程实现生产者-消费者协作模型吗? 原创

简介: 《重学Java高并发》之“摸底考试”:你会使用多线程实现生产者-消费者协作模型吗?原创

1、消费者/生产者场景


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


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


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

1142bb4880382433ea13659953c91c3c.png

2、代码实现


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


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

05c9d90667c8822956216c0bd9af88c2.png

其类的职责说明如下:


  • Bakery 面包厂仓库,主要用来存放面包。
  • BreadWork 面包生产工人
  • BreadConsume 面包消费工人
  • Bread 面包


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


2.1 Bakery核心实现


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

538aa54b5ab5b06740bb6ea857c0791b.png

其核心要点解释如下:


  • Object bakeryLock 锁对象,主要是用来保护List< Bread> 数据结构,众所周知,ArrayList是多线程不安全的,也就是说多个线程对其进行访问,必须加锁,这里之所以单独创建一个对象,主要是想突出锁概念,在代码中,其实可以用 synchronized(breads) 来代替。
  • put方法 该方法是被面包生产者调用,向面包厂中添加面包,但是面包厂的容量是有限的,即生产者不能一直往里面添加,即当达到最大容量后,需要阻止生产者继续往里面添加,故这里涉及到条件等待与wait方法,细细说明如下:
  • 访问breads数据结构之前,先使用synchronized进行保护,即加锁。
  • 如果仓库已满,需要调用锁对象的wait方法,调用锁对象的线程,也就是生产者线程会被阻塞,需要等待其他线程的唤醒,唤醒后才能继续执行后续代码。
  • 如果仓库还有空间,则向仓库中添加一个面包,此时另外一个隐含的条件将满足:仓库中已经有面包了,而消费者可能会因为仓库中没有面包而阻塞,故这里需要调用锁对象的notify或nofifyAll方法,唤醒等待的消费者。
  • get方法 该方法主要是被面包消费者调用,从面包中获取面包,但要能获取面包也是有条件的:仓库中存在面包,否则需要阻塞等待消费者创建面包,故这里的要点如下:
  • 如果仓库中没有面包,调用锁对象的wait方法,则消费者线程将进入阻塞状态,其具体实现是消费者线程对象会放在锁对象的条件等待队列,将等待其他线程调用锁的notify或notifyAll。
  • 如果有面包,则从中消费一个面包,此时另外一个隐含的条件将满足:仓库中已经有新的空间存放新的面包,故此时应该调用锁对象的notify或notifyAll,唤醒生产者。


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


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

110d52efda286c1266c108cbb4af0b13.png

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


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


2.3 运行效果与进阶


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

1ab0e0f373c951d6a5d37d9ea3ca333e.png

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


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

相关文章
|
9月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
390 0
|
10月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
706 5
|
10月前
|
监控 搜索推荐 Java
Java 多线程最新实操技术与应用场景全解析:从基础到进阶
本文深入探讨了Java多线程的现代并发编程技术,涵盖Java 8+新特性,如CompletableFuture异步处理、Stream并行流操作,以及Reactive编程中的Reactor框架。通过具体代码示例,讲解了异步任务组合、并行流优化及响应式编程的核心概念(Flux与Mono)。同时对比了同步、CompletableFuture和Reactor三种实现方式的性能,并总结了最佳实践,帮助开发者构建高效、扩展性强的应用。资源地址:[点击下载](https://pan.quark.cn/s/14fcf913bae6)。
536 3
|
11月前
|
算法 Java 调度
Java多线程基础
本文主要讲解多线程相关知识,分为两部分。第一部分涵盖多线程概念(并发与并行、进程与线程)、Java程序运行原理(JVM启动多线程特性)、实现多线程的两种方式(继承Thread类与实现Runnable接口)及其区别。第二部分涉及线程同步(同步锁的应用场景与代码示例)及线程间通信(wait()与notify()方法的使用)。通过多个Demo代码实例,深入浅出地解析多线程的核心知识点,帮助读者掌握其实现与应用技巧。
180 1
|
9月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
417 83
|
6月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
272 6
|
11月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
406 0
|
7月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
494 16
|
6月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
670 0
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
276 26