中间件核心技术与实战
丁威
中通快递资深架构师,RocketMQ 社区首席布道师
19674 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 33 讲
中间件核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

05 | 多线程:多线程编程有哪些常见的设计模式?

你好,我是丁威。
从这节课开始,我们开始学习 Java 多线程编程。
多线程是很多人在提升技术能力的过程中遇到的第一个坎,关于这部分的资料在网络上已经很多了,但是这些资料往往只重知识点的输出,很少和实际的生产实践相挂钩。但是我不想给你机械地重复“八股文”,接下来的两节课,我会结合这些年来在多线程编程领域的经验,从实际案例出发,带你掌握多线程编程的要领,深入多线程的底层运作场景,实现理解能力的跃升。

如何复用线程?

线程是受操作系统管理的最核心的资源,反复创建和销毁线程会给系统层面带来比较大的开销。所以,为了节约资源,我们需要复用线程,这也是我们在多线程编程中遇到的第一个问题。那怎么复用线程呢?
我们先来看一小段代码:
Thread t = new Thread(new UserTask());
请你思考一下,这段代码会创建一个操作系统线程吗?
答案是不会。这段代码只是创建了一个普通的 Java 对象,要想成为一个真实的线程,必须调用线程的 start 方法,让线程真正受操作系统调度。而线程的结束和 run 方法的执行情况有关,一旦线程的 run 方法结束运行,线程就会进入消亡阶段,相关资源也会被操作系统回收。
所以要想复用线程,一个非常可行的思路就是,不让 run 方法结束。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了多线程编程中的常见设计模式和实践经验,为读者提供了全面的多线程编程指南。首先介绍了线程复用模型和优雅停止线程的方法,强调了避免直接使用Executors相关API创建线程池的重要性,并推荐了使用new的方式创建线程,并给线程指定可阅读的名称。此外,文章还讨论了如何选择阻塞队列,建议在Request-Response等需要用户交互的场景中使用有界队列,以保证系统的可用性。同时,还分享了在文件下载、系统发布等耗时请求场景中使用线程池的实践经验。 另外,文章介绍了Future模式,通过实例代码和解读,读者可以了解如何使用线程池和Callable接口来实现Future模式,以及在中间件开发领域中使用CountDownLatch来实现轻量级的Future模式。这些实例帮助读者理解Future模式的实际应用和巧妙之处,为他们在实际开发中提供了有益的参考。 此外,还介绍了生产者-消费者模式的工作原理和使用场景,并提到了高并发架构设计中的底线思维:限流机制。最后,文章详细介绍了如何使用信号量来实现限流,在多线程环境中避免信号量的超发,以防止出现意外情况。 总的来说,本文通过深入讨论多线程编程的设计模式和实践经验,使读者能够快速掌握多线程编程的技术要点,并在实际开发中应用这些知识。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《中间件核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(11)

  • 最新
  • 精选
  • 小豹哥
    真干, 有点 消化不了 ,这个太顶了

    作者回复: 谢谢,你的认可是我最大的动力,如果对其中某一个点不太理解的话,我们多多交流,在交流过程中,我相信能碰出更大的火花,我们共同成长,进步。

    2022-06-24归属地:上海
    9
  • 末日,成欢
    场景题: 1. 有个仓库,该仓库存储容量有限 2. 【线程A】工人生产面包给仓库,仓库容量++, 达到容量后,工人不需要再生产 3. 【线程B】顾客购买面包,仓库容量--,容量没有时,无法购买 我一步一步逻辑大概是这样的: 1. 将仓库封装为一个类,该类中有一个成员属性: 仓库容量。 2. 提供线程安全的添加容量、扣减容量的方法。也就是使用synchronized保护起来 3. 通过这样可以让添加、扣减是线程安全的。 不会出现线程安全性问题。 但是通过这样还是存在问题的。 也就是工人生成面包达到容量后,我们通过提供的添加方法添加失败后,可以知道已经到达仓库容量了,此时无需生产。 而什么时候工人继续生产是不知道的,可能需要一个while(true)一直调用添加方法判断是否有顾客购买。 在这种场景下,可能会顾客一直都不来买的情况,就会导致一直死循环的获取锁,判断,释放锁。 极其耗费CPU资源。 而最好的方案就是使用,等待-通知机制。 也就是容量到达阈值时,线程进入阻塞状态,容器不够时,线程被唤醒重新运行。 这里可以使用多线程中的【保护性暂挂模式】 该模式也就是wait、notifyAll的一个规范实现,通过这种机制可以大大降低获取线程的无效动作。【条件不满足时,就阻塞】

    作者回复: 你好,回答的非常不错,思路清晰👍

    2022-07-23归属地:上海
    3
  • 雨落~紫竹
    这篇 最后的干货 真让眼前一亮

    作者回复: 谢谢认可🙏

    2022-06-22归属地:上海
    1
  • Sudouble
    多线程的门门道道不少。认识到了上层设计,决定下层实现。

    作者回复: 嗯,对的,按照我的经验,多线程最重要的其实就是理解线程安全,以及锁的实现原理(通过研读juc源码),阅读juc框架,一定会给我们带来不一样的感悟。

    2022-12-28归属地:福建
  • MMMMMCCLXXVII
    public void shutdown() { if(running.compareAndSet(true, false)) { System.out.println(Thread.currentThread() + " is stoped"); } }​ 这个方法不应该去调用interrupt吗?
    2023-10-17归属地:江苏
  • Geek_341657
    老师您好,我想问一下使用信号量进行限流的部分,为什么信号量会超发呢
    2023-02-26归属地:江苏
  • 最摇摆的鱼
    catch (Throwable e) { e.printStackTrace(); } 这里有问题吧?这不是吞掉了taskQueue.take()抛出的InterruptedException吗?这样在等待task的时候就不会被中断了。
    2022-10-26归属地:上海
  • dudu-benny
    “为了实现 MySQL 增量同步,Canal 线程源源不断地将 MySQL 数据写入到阻塞队列,然后目标端线程从队列中读取数据并写入到 MQ ” 这里为什么不直接从canal 读取的数据直接写入 MQ , 省去队列带来的可能阻塞和队里阻塞带来canal的获取速度等等问题
    2022-07-16
    1
  • dudu-benny
    1.Future 可以理解为nio 线程模型,同步非阻塞io模型,reactor模式 netty的应用等 2.工厂是生产者 顾客是消费者 仓库是有界队列,其实可以使用我们刚刚学习的LinkedBlockingQueue来实现
    2022-07-16
  • 浅qian的痕迹
    场景题可以使用 wait()和notifyAll()来实现,设置一个容量阈值,if当前容量达到容量阈值,就wait(),让生产线程阻塞, 当顾客购买面包后,仓库容量会得到释放,就notifyAll(), 让生产线程继续生产
    2022-07-11
收起评论
显示
设置
留言
11
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部