Java 性能调优实战
刘超
前金山软件技术经理
59174 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
开篇词 (1讲)
模块一 · 概述 (2讲)
结束语 (1讲)
Java 性能调优实战
15
15
1.0x
00:00/00:00
登录|注册

30 | 生产者消费者模式:电商库存设计优化

优化线程阻塞的性能问题
消息队列
缓存库存
思考题
生产者消费者优化电商库存设计
BlockingQueue实现生产者消费者
Lock中Condition的await/signal/signalAll实现生产者消费者
Object的wait/notify/notifyAll实现生产者消费者
生产者消费者模式

该思维导图由 AI 生成,仅供参考

你好,我是刘超。
生产者消费者模式,在之前的一些案例中,我们是有使用过的,相信你有一定的了解。这个模式是一个十分经典的多线程并发协作模式,生产者与消费者是通过一个中间容器来解决强耦合关系,并以此来实现不同的生产与消费速度,从而达到缓冲的效果。
使用生产者消费者模式,可以提高系统的性能和吞吐量,今天我们就来看看该模式的几种实现方式,还有其在电商库存中的应用。

Object 的 wait/notify/notifyAll 实现生产者消费者

第 16 讲中,我就曾介绍过使用 Object 的 wait/notify/notifyAll 实现生产者消费者模式,这种方式是基于 Object 的 wait/notify/notifyAll 与对象监视器(Monitor)实现线程间的等待和通知。
还有,在第 12 讲中我也详细讲解过 Monitor 的工作原理,借此我们可以得知,这种方式实现的生产者消费者模式是基于内核来实现的,有可能会导致大量的上下文切换,所以性能并不是最理想的。

Lock 中 Condition 的 await/signal/signalAll 实现生产者消费者

相对 Object 类提供的 wait/notify/notifyAll 方法实现的生产者消费者模式,我更推荐使用 java.util.concurrent 包提供的 Lock && Condition 实现的生产者消费者模式。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

生产者消费者模式在电商库存设计中的优化应用是本文的重点。文章首先介绍了两种实现方式:使用Object的wait/notify/notifyAll和Lock中Condition的await/signal/signalAll,并分析了它们的优缺点。随后通过代码案例展示了如何使用Lock和Condition来优化生产者消费者模式,将生产者和消费者的等待条件和锁资源分离,以提高系统并发性能。另外,文章还介绍了使用BlockingQueue实现生产者消费者模式的简单明了方式,并通过案例展示了如何使用LinkedBlockingQueue来实现生产者消费者模式。通过对比不同实现方式的优缺点和代码案例展示的优化方法,读者可以快速了解生产者消费者模式在电商库存设计中的应用及优化方法。此外,文章还提到了生产者消费者模式在缓冲高并发数据库扣除库存压力和处理执行任务时间较长的场景中的应用,以及思考题引发了读者对线程阻塞性能问题的思考。整体而言,本文通过详细的技术讲解和案例分析,为读者提供了深入了解生产者消费者模式在电商库存设计中的应用及优化方法的机会。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 性能调优实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(34)

  • 最新
  • 精选
  • Jxin
    1.生产消费模式用信号量也能玩。 2.生产者这边的优化思路应该是提高响应速度和增加资源。提高响应速度就是尽量降低生产逻辑的耗时,增加资源就是根据业务量为该生产者单独线程池并调整线程数。至于限流和令牌桶感觉都是降级处理,属于规避阻塞场景而非解决阻塞场景,应该不在答案范围内吧。 3.对于进程内生产消费模式,大规模,量大的数据本身就不适合,毕竟内存空间有限,消息堆积有限,所以量级达到一定指标就采用跨进程方式,比如kafka和rocketmq。同时,进程内生产消费模式,异常要处理好,不然可能会出现消息堆积和脏数据,毕竟mq的消费确认和重试机制都是开箱即用,而我们得自己实现和把关。

    作者回复: 看来有实战经验👍🏻

    2019-07-31
    3
    27
  • 杨俊
    我的理解是库存放缓存,用户提交订单在缓存扣减库存,用户端能够快速返回显示订单提交成功并支付,然后只有支付成功之后才会利用队列实际的扣减数据库库存是吗?要是不支付会在缓存补回库存吧,应该会限时支付吧

    作者回复: 对的

    2019-07-30
    2
    18
  • QQ怪
    在网关层中把请求放入到mq中,后端服务从消费队列中消费消息并处理;或者用有固定容量的消费队列的令牌桶,令牌发生器预估预计的处理能力,匀速生产放入令牌队列中,满了直接丢弃,网关收到请求之后消费一个令牌,获得令牌的请求才能进行后端秒杀请求,反之直接返回秒杀失败

    作者回复: 大家都一致想到了限流,限流是非常必要的,无论我们的程序优化的如何,还是有上限的,限流则是一种兜底策略。 除了这个,我们还可以使用协程来优化线程由于阻塞等待带来的上下文切换性能问题,可以回顾第19讲,我们也用协程实现过生产者消费者模式。

    2019-07-30
    5
    9
  • JasonK
    你好,刘老师,最近生产上一个服务,老是半夜cpu飙升,导致服务死掉,排查问题看了下,都是GC task thread#15 (ParallelGC) 线程占用CPU资源,这是为什么?而且同样的服务我布了两台机器,一台服务会死掉,一台不会。请老师解惑。

    作者回复: 导致CPU飙升只是一个性能的直接表现,是不是有对象一直在创建,所以导致一直在GC。建议打开dump日志查看具体的内存使用情况以及对象的创建分布情况。

    2019-07-31
    3
    3
  • 明翼
    老师,生产者和消费者的锁分开没问题吗?都是用的同一个队列?

    作者回复: 这里同步更新下,新增了以下代码作为实时库存: private AtomicInteger inventory = new AtomicInteger(0); 我们这里是基于LinkedList来存取库存的,虽然LinkedList是非线程安全,但我们新增是操作头部,而消费则是操作队列的尾部,理论上来说没有线程安全问题。而库存的实际数量inventory是基于AtomicInteger(CAS锁)线程安全类实现,即可以保证原子性,也可以保证消费者和生产者之间是可见的。

    2019-07-30
    2
    3
  • 罗洲
    生产方的高并发优化,我们可以参考下tomcat的设计,tomcat设计了线程池来进行请求接收,有最小线程数,最大线程数,同时还有一个有界的工作队列,来接收超过线程数的请求,当工作队列满了后可以选择拒绝或者丢弃处理。

    作者回复: 赞,很好的参考例子

    2019-07-30
    2
    3
  • 撒旦的堕落
    网关与服务之间增加令牌桶 或者mq 以保护秒杀服务不会被大的流量压垮 可以么

    作者回复: 可以的,很通用的一种解决方案

    2019-07-30
    2
  • 2102
    增加消费者

    作者回复: 增加消费者是一种方式

    2019-10-19
    1
  • 怎☞劰☜叻
    老师,我看到你上面说用协程来优化!我们这边有个服务属于业务网关,要聚合多个下有的数据,涉及大量的网络io,之前是使用多线程并行调用多个下有,现在发现线程越来越多,遇到了瓶颈!希望用协程来改进方案~但是我在网上找到的一些java协程开源组件,文档和生态都不是很健全,希望老师能给出一些建议~ 非常感谢

    作者回复: 建议再等等官方的协成组件,或改用go实现,目前Java的一些第三方开源组件的生产环境的实践以及性能验证有待考验,如果不介意当小白鼠,也可以试试这些第三方协成组件。

    2019-08-09
    1
  • 一个卖火柴的老男人
    商品从数据库压入redis 缓存。 同时库存压入redis,用商品ID作为key,用list模拟队列【1001,1001,1001】用商品🆔做队列元素,100件库存,那就存100个🆔在队列中,扣库存的时候,判断队列大小,可以防止超卖。所有都是靠redis的单线程原子操作保证,可行不

    作者回复: 可行

    2019-08-03
    2
    1
收起评论
显示
设置
留言
34
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部