TL;DR(30 秒扫完)
- 并行 = 同一时间点上多个任务真的同时执行,需要多核 CPU;并发 = 多个任务在一段时间窗口内交替执行,单核通过时间片实现
- 线程安全只在"多线程 + 共享可变资源"同时成立时有意义;无共享/无状态/无状态可变都不构成安全问题
- 线程安全三个层次:互斥(一次一个线程改)、可见性(改了对别人可见)、有序性(指令不重排破坏语义)
- 实现手段谱系:synchronized / Lock / volatile / AtomicXxx / ThreadLocal / 无状态化 / 单线程化
关键结论
结论 A并行是硬件能力(多核),并发是调度策略(分时);单核只能并发不能并行
结论 B线程安全的本质是"多线程访问共享可变资源时,任意时刻执行结果都符合预期"
结论 C线程安全三个层次——互斥(Monitor 互斥)、可见性(解锁刷主存+加锁 reload)、有序性(JIT 禁止指令重排)
结论 D无状态化是最高级的"线程安全"——Redis 单线程、Netty 单 Reactor、纯函数都靠"不共享可变"来消除问题
完整讲解(费曼四步)
STEP 1 · 概念
并发和并行是两个不同的概念。并行强调"同一时间点真的同时",并发强调"一段时间窗口内交替执行"。线程安全是指在多线程访问共享资源时,任意时刻的执行结果都符合预期。STEP 2 · 大白话
类比:想象一个厨房只有一个灶台(单核)。你做了三道菜(三个任务),但每次只能炒一道,炒完一道再炒下一道——这是并发,看起来三道菜一起在做,其实同一时刻只做一道。>
如果厨房有三个灶台(多核),三道菜同时开火——这才是并行。>
线程安全就像"多人共用一个工具箱"——如果两人同时拿同一把扳手,可能打架(竞态);如果规定"一次只能一人拿"(互斥),"谁拿了要在墙上签字"(可见性),"按顺序拿"(有序性),就是线程安全。
STEP 3 · 底层
并发 vs 并行
| 维度 | 并发 | 并行 |
|---|---|---|
| 同一时间点 | 只执行一个任务 | 多个任务真正同时执行 |
| 硬件要求 | 单核即可 | 必须多核 |
| 调度机制 | OS 分时调度(time-slicing) | OS 跨核分配 |
| 切换代价 | 有上下文切换开销 | 无切换(各核独立) |
| Java 体现 | 线程、线程池、虚拟线程 | 多核 CPU 上的并行流、ForkJoinPool |
线程安全的三个层次
| 层次 | 含义 | 典型问题 | 解决手段 |
|---|---|---|---|
| 互斥 | 一次只有一个线程能修改 | 读-改-写竞态(i++ 丢更新) | synchronized、ReentrantLock |
| 可见性 | 改了对其他线程立即可见 | CPU 缓存不一致、JIT 缓存 | volatile(MESI + lock 前缀)、synchronized |
| 有序性 | 指令不能重排破坏语义 | DCL 单例半构造实例 | volatile 内存屏障、synchronized |
实现手段谱系
| 手段 | 类型 | 适用场景 |
|---|---|---|
| synchronized | 强制同步 | 低竞争、临界区短 |
| ReentrantLock | 强制同步(用户态) | 高竞争、需中断/超时/公平 |
| volatile | 弱化共享 | 单变量读写、状态标志位 |
| AtomicXxx | 无锁原子 | 单变量复合操作(i++) |
| ThreadLocal | 弱化共享 | 线程隔离数据 |
| 不可变对象 | 弱化共享 | String、不可变 DTO |
| 单线程化 | 消除共享 | Redis 主线程、Netty 单 Reactor |
STEP 4 · 简化
一句话总结:并行是"真同时"(多核),并发是"假同时"(分时);线程安全 = 互斥 + 可见性 + 有序性,本质是"多线程共享可变资源时的正确性"。
常见误区
并发 = 大量请求同时处理
并发 = 多任务交替执行,不限定数量
线程安全 = 加锁
线程安全有三种实现(互斥/可见性/有序性),加锁只是互斥的一种手段
无锁 = 线程安全
无锁(CAS)只是原子操作,不保证复合操作的互斥
synchronized 是公平锁
synchronized 是非公平锁
延伸追问
Java 中有哪些并发编程工具?分别解决什么问题?
线程(基础)、线程池(复用)、ExecutorService(抽象)、CompletableFuture(编排)、ForkJoinPool(分治)、AtomicXxx/CAS(无锁)、虚拟线程(轻量)
并发和并行哪个性能更好?
并行扩展性线性(受核数约束),并发受上下文切换开销影响;Amdahl 定律限制并行加速比
虚拟线程为什么让并发更简单?
M:N 调度(少量载体线程映射大量虚拟线程)、栈内存只占几十 KB、同步阻塞不占平台线程
速查表
并行 vs 并发:
并行 = 真同时(多核) 并发 = 假同时(分时)
并行强调"时间点" 并发强调"时间窗口"
并行是硬件能力 并发是调度策略
线程安全三层:
互斥 = 一次一个线程改 → synchronized / Lock
可见性 = 改了对别人可见 → volatile / synchronized
有序性 = 指令不重排 → volatile 屏障 / synchronized
实现手段:
强同步:synchronized / ReentrantLock
无锁:AtomicXxx / CAS
弱化:ThreadLocal / 不可变 / 无状态
消除:单线程化(Redis / Netty)
Anki 候选卡片
Q: 并发和并行的本质区别是什么?
A: 并行=同一时间点真同时(多核);并发=一段时间窗口交替(分时调度)
Q: 线程安全的三个层次?
A: 互斥(一次一个线程改)、可见性(改了对别人可见)、有序性(指令不重排)
Q: 线程安全的前置条件?
A: 多线程 + 共享可变资源,两者同时成立才有意义
Q: synchronized 是公平锁还是非公平锁?
A: 非公平锁
关联题目
- [ ] 《什么是并发,什么是并行?》— 2026-09-26 Round 1 Q1, ⭐⭐⭐⭐
- [ ] 《什么是线程安全》— 2026-09-26 Round 1 Q2, ⭐⭐⭐
关联知识
并行=真同时(多核);并发=假同时(分时);线程安全=互斥+可见性+有序性;术语速查覆盖 Java 并发高频报错点
厨房类比:并行像多灶台真同时炒菜;并发像单灶台时间片炒菜;线程安全像规定一次只能一个人进厨房、改动立刻通知所有人、按顺序操作不颠倒
✦ 记 忆 口 诀 ✦
并行硬件/并发调度;线程安全三层=互斥/可见/有序;JMM 规范/JVM 实现;守护线程=JVM 退出条件;Thread.State 6 种
关键可视化
并发 vs 并行
flowchart LR
subgraph C["并发(假同时)"]
T["单核"] --> S["时间片调度"]
S --> R["多任务交替执行"]
end
subgraph P["并行(真同时)"]
M["多核"] --> S2["同一时间点执行"]
S2 --> R2["真同时"]
end
C -.互补.-> P
P -.约束.-> A["Amdahl 定律<br/>加速比受串行部分限制"]线程安全三层
flowchart TB S["线程安全<br/>= 多线程共享可变资源时正确"] --> M["互斥<br/>一次一个线程改<br/>→ synchronized / Lock"] S --> V["可见性<br/>改了对别人可见<br/>→ volatile / synchronized"] S --> O["有序性<br/>指令不重排<br/>→ volatile 屏障 / synchronized"] S -.消除.-> E["无状态化 / 单线程化<br/>→ Redis / Netty"]
Thread.State 6 种枚举
flowchart TB N["NEW<br/>new 后未 start"] --> RUN["RUNNABLE<br/>start() 后<br/>(就绪+运行合一)"] RUN --> B["BLOCKED<br/>抢 synchronized 锁失败<br/>(自动进入)"] RUN --> W["WAITING<br/>显式 wait/join/park<br/>无超时"] RUN --> TW["TIMED_WAITING<br/>显式 wait/sleep/join<br/>带超时"] RUN --> T["TERMINATED<br/>run() 返回或异常"] B -.抢到锁.-> RUN W -.notify/unpark.-> RUN TW -.超时/notify.-> RUN style N fill:#e0e0e0 style T fill:#e0e0e0
守护线程 vs 用户线程
flowchart LR
subgraph JVM["JVM 退出条件"]
C1["检查是否还有用户线程"]
C1 --> Yes["有 → 继续运行"]
C1 --> No["无 → JVM 退出"]
No --> D["守护线程被强制终止<br/>finally 可能不执行"]
end
D -.典型.-> GC["GC 线程 / JIT / Finalizer / Reference Handler"]知识关系
⬆️ 前置(Prerequisite)
暂无
🎯 概念
📏 规则
⚠️ 误区
🔍 追问
✨ 口诀
共 0 张卡,点击翻面