10张图告诉你多线程那些破事!

发布时间:2026/11/5 16:10:44

10张图告诉你多线程那些破事! 在实际工作中错误使用多线程非但不能提高效率还可能使程序崩溃。以在路上开车为例在一个单向行驶的道路上每辆汽车都遵守交通规则这时候整体通行是正常的。『单向车道』意味着『一个线程』『多辆车』意味着『多个job任务』。单线程顺利同行如果需要提升车辆的同行效率一般的做法就是扩展车道对应程序来说就是『加线程池』增加线程数。这样在同一时间内通行的车辆数远远大于单车道。多线程顺利同行然而成年人的世界没有那么完美车道一旦多起来『加塞』的场景就会越来越多出现碰撞后也会影响整条马路的通行效率。这么一对比下来『多车道』确实可能比『单车道』要慢。多线程故障防止汽车频繁变道加塞可以采取在车道间增加『护栏』那在程序的世界该怎么做呢程序世界中多线程遇到的问题归纳起来就是三类『线程安全问题』、『活跃性问题』、『性能问题』接下来会讲解这些问题以及问题对应的解决手段。线程安全问题有时候我们会发现明明在单线程环境中正常运行的代码在多线程环境中可能会出现意料之外的结果其实这就是大家常说的『线程不安全』。那到底什么是线程不安全呢往下看。原子性举一个银行转账的例子比如从账户A向账户B转1000元那么必然包括2个操作从账户A减去1000元往账户B加上1000元两个操作都成功才意味着一次转账最终成功。试想一下如果这两个操作不具备原子性从A的账户扣减了1000元之后操作突然终止了账户B没有增加1000元那问题就大了。银行转账这个例子有两个步骤出现了意外后导致转账失败说明没有原子性。原子性即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断要么就都不执行。原子操作即不会被线程调度机制打断的操作没有上下文切换。在并发编程中很多操作都不是原子操作出个小题目i 0; // 操作1 i; // 操作2 i j; // 操作3 i i 1; // 操作4上面这四个操作中有哪些是原子操作哪些不是的不熟悉的人可能认为这些都是原子操作其实只有操作1是原子操作。操作1对基本数据类型变量的赋值是原子操作操作2包含三个操作读取i的值将i加1将值赋给i操作3读取j的值将j的值赋给i操作4包含三个操作读取i的值将i加1将值赋给i在单线程环境下上述四个操作都不会出现问题但是在多线程环境下如果不通过加锁操作往往可能得到意料之外的值。在Java语言中通过可以使用synchronize或者lock来保证原子性。可见性talk is cheap先show一段代码class Test { int i 50; int j 0; public void update() { // 线程1执行 i 100; } public int get() { // 线程2执行 j i; return j; } }线程1执行update方法将 i 赋值为100一般情况下线程1会在自己的工作内存中完成赋值操作却没有及时将新值刷新到主内存中。这个时候线程2执行get方法首先会从主内存中读取i的值然后加载到自己的工作内存中这个时候读取到i的值是50再将50赋值给j最后返回j的值就是50了。原本期望返回100结果返回50这就是可见性问题线程1对变量i进行了修改线程2没有立即看到i的新值。可见性指当多个线程访问同一个变量时一个线程修改了这个变量的值其他线程能够立即看得到修改的值。如上图每个线程都有属于自己的工作内存工作内存和主内存间需要通过store和load等进行交互。为了解决多线程可见性问题Java语言提供了volatile这个关键字。当一个共享变量被volatile修饰时它会保证修改的值会立即被更新到主存当有其他线程需要读取时它会去内存中读取新值。而普通共享变量不能保证可见性因为变量被修改后什么时候刷回到主存是不确定的另外一个线程读的可能就是旧值。当然Java的锁机制如synchronize和lock也是可以保证可见性的加锁可以保证在同一时刻只有一个线程在执行同步代码块释放锁之前会将变量刷回至主存这样也就保证了可见性。关于线程不安全的表现还有『有序性』这个问题会在后面的文章中深入讲解。活跃性问题上面讲到为了解决可见性问题我们可以采取加锁方式解决但是如果加锁使用不当也容易引入其他问题比如『死锁』。在说『死锁』前我们先引入另外一个概念活跃性问题。活跃性是指某件正确的事情最终会发生当某个操作无法继续下去的时候就会发生活跃性问题。概念是不是有点拗口如果看不懂也没关系你可以记住活跃性问题一般有这样几类死锁活锁饥饿问题。1死锁死锁是指多个线程因为环形的等待锁的关系而永远的阻塞下去。一图胜千语不多解释。2活锁死锁是两个线程都在等待对方释放锁导致阻塞。而活锁的意思是线程没有阻塞还活着呢。当多个线程都在运行并且修改各自的状态而其他线程彼此依赖这个状态导致任何一个线程都无法继续执行只能重复着自身的动作和修改自身的状态这种场景就是发生了活锁。![](/Users/ray/Library/Application Support/typora-user-images/image-20210408232019843.png)如果大家还有疑惑那我再举一个生活中的例子大家平时在走路的时候迎面走来一个人两个人互相让路但是又同时走到了一个方向如果一直这样重复着避让这俩人就是发生了活锁学到了吧嘿嘿。3饥饿如果一个线程无其他异常却迟迟不能继续运行那基本是处于饥饿状态了。常见有几种场景:高优先级的线程一直在运行消耗CPU所有的低优先级线程一直处于等待一些线程被永久堵塞在一个等待进入同步块的状态而其他线程总是能在它之前持续地对该同步块进行访问有一个非常经典的饥饿问题就是哲学家用餐问题如下图所示有五个哲学家在用餐每个人必须要同时拿两把叉子才可以开始就餐如果哲学家1和哲学家3同时开始就餐那哲学家2、4、5就得饿肚子等待了。性能问题前面讲到了线程安全和死锁、活锁这些问题会影响多线程执行过程如果这些都没有发生多线程并发一定比单线程串行执行快吗答案是不一定因为多线程有创建线程和线程上下文切换的开销。创建线程是直接向系统申请资源的对操作系统来说创建一个线程的代价是十分昂贵的需要给它分配内存、列入调度等。线程创建完之后还会遇到线程上下文切换。CPU是很宝贵的资源速度也非常快为了保证雨露均沾通常为给不同的线程分配时间片当CPU从执行一个线程切换到执行另一个线程时CPU 需要保存当前线程的本地数据程序指针等状态并加载下一个要执行的线程的本地数据程序指针等这个开关被称为『上下文切换』。一般减少上下文切换的方法有无锁并发编程、CAS 算法、使用协程等。有态度的总结多线程用好了可以让程序的效率成倍提升用不好可能比单线程还要慢。用一张图总结一下上面讲的
延伸阅读

更多相关文章

2026/10/31 2:07:41

OpenAI Codex实战教程:零基础入门代码生成与API集成

这次我们来看一个面向零基础用户的 Codex 完整实战教程。Codex 作为 OpenAI 推出的代码生成模型,能够根据自然语言描述生成多种编程语言的代码,对于编程初学者、快速原型开发和自动化脚本编写都有很大帮助。本教程重点解决三个问题:第一&…

2026/10/30 3:02:07

多维聚合实战:滚动窗口与业务逻辑嵌入的生产级实现

1. 项目概述:为什么多维聚合不是“加个groupby”就能搞定的事我在银行数据平台组干了八年,从最早用SQL写几十行嵌套子查询做客户分层,到现在每天在Jupyter里调试pandas的agg链式调用,踩过的坑比写的代码还多。今天这篇讲的“多维聚…

2026/11/5 15:00:49

基于Hadoop+Spark的民宿智能推荐系统设计与实现

1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享…

2026/11/5 14:10:30

ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配RS485,解决远距离温度监控难题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 13:55:19

GD32E10x官方例程入门:从GPIO点灯到串口调试

简介:这份GD32E10x系列官方例程面向嵌入式硬件与单片机开发者,基于ARM Cortex-M3内核,汇集兆易创新GD32E10x的示例代码,适合工业控制、消费电子、通信设备等场景的学习、验证与二次开发。压缩包共935个文件,约11.16MB&…

2026/11/5 13:50:18

基于Django的运维平台实战:资产、发布、巡检、告警一体化

简介:这份资源是一套基于Django框架开发的运维平台源码,面向具备Python基础、希望了解Web运维系统实现思路的开发者与运维人员,可用于学习主机管理、服务监控、日志查看、任务调度、权限控制、资源统计、脚本执行与故障告警等常见运维模块的落…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑