线程安全、线程同步、竞态条件

一、线程安全(Thread Safety)

定义:

当多个线程同时访问一个对象/数据时,不管操作系统怎么调度这些线程,也不管它们的执行顺序怎么穿插交织,最终的结果都必须是正确的,不会出现数据错乱、重复、丢失等问题。

核心就一句话:并发执行,结果仍对。

类比: 就像银行转账。你同时用手机APP和网页端各转一笔钱,银行系统必须保证:

  • 不会因为你两个渠道同时操作,就把你的钱算错

  • 不会转出去双倍的钱

  • 也不会吞掉你的钱

你的系统现在线程不安全,因为两个用户同时审核,生成了两张入库单------结果错了。


二、线程同步(Thread Synchronization)

定义:

协调多个线程对共享资源的访问顺序,让它们"有规矩"地执行,避免互相干扰、互相覆盖。

给共享资源加把锁,保证同一时刻只有一个线程可以访问他。

核心就一句话:给并发访问定规矩。

类比: 想象一个单向通行的隧道:

  • 没有同步:两辆车对头开,直接在隧道中间撞车(数据冲突)

  • 有同步:要么装红绿灯轮流走(互斥),要么分道行驶各走各的(隔离)

同步只是实现"线程安全"的一种手段,不是唯一手段。除了"排队访问"(同步),还可以通过"各用各的"(ThreadLocal)或"只读不写"(不可变对象)来实现安全。


三、竞态条件(Race Condition)

定义:

多个线程同时读写共享资源,最终的结果取决于它们执行的"先后顺序"和"时机"------就像赛跑,谁先谁后到终点,结果完全不同。

核心就一句话:结果看运气,取决于谁先执行完。


"检查-执行"模式:竞态条件的温床

在业务代码里,最常见的就是 "检查-然后-执行"(Check-Then-Act) 模式:

复制代码
// 第1步:检查条件
if (单据状态.equals("待审核")) {
    // 第2步:执行动作
    updateStatus("已审核");
    createStockInOrder();
}

单线程下,这段代码毫无问题。但多线程下,"检查"和"执行"是两个独立的操作,中间存在时间窗口。另一个线程完全可以趁这个空档插入进来,基于同样过期的检查结果也执行一遍。


你的审核Bug时间线

复制代码
时间点        用户A的线程                    用户B的线程                 数据库状态
─────────────────────────────────────────────────────────────────────────────────────
09:00:00.100  【检查】查询状态 → 待审核                                  待审核
09:00:00.150                                【检查】查询状态 → 待审核     待审核
              ↑                                                        ↑
              └────────── 两人都通过了"检查",都准备执行 ──────────────┘

09:00:00.200  【执行】更新为已审核,生成入库单①                          已审核
09:00:00.250                                【执行】更新为已审核,
                                            生成入库单②                 已审核
                                            ↑
                                            └─ B也执行了!因为B检查时状态还是"待审核"

同样的代码,同样的操作,只是因为线程执行的时机不同:

执行顺序 结果
A 完整做完(检查+执行)→ B 再做 ✅ 只生成一张入库单
A 检查完 → B 检查完 → A 执行 → B 执行 ❌ 生成两张入库单

一句话总结

竞态条件 = "检查-执行"模式在多线程下被并发打断,另一个线程基于过期的检查结果执行了动作,导致程序正确性取决于不可控的线程调度顺序。


四、三者之间的关系

复制代码
┌─────────────────────────────────────────────┐
│                                             │
│   竞态条件(Race Condition)                 │
│   ↓ 是问题的根源                             │
│   "多线程读写共享资源,时机不确定导致结果错乱"    │
│                                             │
│            ↓ 需要用手段解决                    │
│                                             │
│   线程同步(Thread Synchronization)         │
│   ↓ 是解决问题的手段                          │
│   "协调线程访问顺序,给并发定规矩"              │
│                                             │
│            ↓ 最终实现目标                      │
│                                             │
│   线程安全(Thread Safety)                  │
│   ↓ 是最终要达到的状态                        │
│   "不管怎么并发,结果始终正确"                 │
│                                             │
└─────────────────────────────────────────────┘

五、结合你的审核Bug串起来

概念 在你的系统里具体表现
竞态条件 用户A和用户B几乎同时查询单据状态,都查到"待审核",然后各自执行了审核逻辑
线程同步缺失 代码里"查询判断"和"更新状态"之间没有加锁或版本控制,两个线程穿插执行没人管
线程不安全 最终结果错误:同一张单据生成了两张入库单,数据库里状态虽然都是"已审核",但业务数据重复了

六、一句话记住三者

竞态条件 = "为什么会出事"(多线程抢资源,时机不可控)
线程同步 = "怎么防止出事"(给并发定规矩,排队或事后检查)
线程安全 = "出事了吗"(最终结果对不对,是目标也是检验标准)

你的Bug修复,本质上就是用线程同步的手段(悲观锁/乐观锁),消除竞态条件,最终实现线程安全

相关推荐
笨拙的老猴子31 分钟前
Spring AI 实战教程(七):Agent 智能体 —— 用电商购物助手学透自主规划与工具执行
java·人工智能·spring
谙弆悕博士33 分钟前
快速学C语言——第19章:C语言常用开发库
c语言·开发语言·算法·业界资讯·常用函数
月落归舟34 分钟前
深入解析Java基础之基础
java·开发语言
折哥的程序人生 · 物流技术专研35 分钟前
《Java 100 天进阶之路》第20篇:Java初始化、构造器、对象创建的过程
java·开发语言·后端·面试
南宫萧幕39 分钟前
基于 Simulink 与 Python 联合仿真的 eVTOL 强化学习全链路实战
开发语言·人工智能·python·算法·机器学习·控制
电魂泡哥41 分钟前
CMS垃圾回收
java·jvm·算法
csbysj20201 小时前
Perl 运算符
开发语言
Amctwd1 小时前
【Python】从Excel中按行提取图片
java·python·excel
啃臭1 小时前
AOP和反射
java·spring boot
西凉的悲伤1 小时前
java 使用PNG图片隐写文件
java·图片隐写·png