跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
代码酷
搜索
搜索
中文(中国大陆)
外观
创建账号
登录
个人工具
创建账号
登录
未登录编辑者的页面
了解详情
贡献
讨论
编辑“︁
线程安全问题
”︁
页面
讨论
大陆简体
阅读
编辑
编辑源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
编辑
编辑源代码
查看历史
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
您的更改会在有权核准的用户核准后向读者展示。
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
{{DISPLAYTITLE:线程安全问题}} {{编程概念导航}} == 概述 == '''线程安全问题'''(Thread Safety)指当多个线程并发访问共享资源时,程序仍能保持正确的行为和数据一致性。若未采取同步措施,可能导致竞态条件(Race Condition)、内存可见性问题或指令重排序等问题,进而引发不可预知的错误。 线程安全的核心挑战在于: * '''原子性''':操作不可被中断(如非原子操作的`i++`) * '''可见性''':线程对共享变量的修改对其他线程立即可见 * '''有序性''':程序执行顺序符合代码逻辑(避免指令重排序) == 典型问题场景 == === 竞态条件示例 === 以下代码展示了一个经典的线程不安全案例: <syntaxhighlight lang="java"> public class Counter { private int count = 0; public void increment() { count++; // 非原子操作(读取-修改-写入) } public int getCount() { return count; } } </syntaxhighlight> 当多个线程同时调用`increment()`时,可能发生以下情况: 1. 线程A读取`count=0` 2. 线程B也读取`count=0` 3. 两者分别执行`count+1`并写回 4. 最终结果可能是`1`而非预期的`2` === 内存可见性问题 === <syntaxhighlight lang="java"> public class VisibilityProblem { private boolean flag = true; public void writer() { flag = false; // 修改可能不会立即对其他线程可见 } public void reader() { while (flag) { // 可能陷入无限循环 } } } </syntaxhighlight> == 解决方案 == === 同步机制 === {| class="wikitable" ! 方法 !! 描述 !! 适用场景 |- | `synchronized` || 通过互斥锁保证原子性和可见性 || 方法/代码块同步 |- | `volatile` || 保证可见性,禁止指令重排序 || 单一变量状态标志 |- | `AtomicXXX` || CAS(Compare-And-Swap)原子类 || 计数器等场景 |} ==== synchronized示例 ==== <syntaxhighlight lang="java"> public class SafeCounter { private int count = 0; public synchronized void increment() { count++; } } </syntaxhighlight> ==== AtomicInteger示例 ==== <syntaxhighlight lang="java"> import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } } </syntaxhighlight> === 线程封闭 === 通过限制数据访问权限避免共享: * '''栈封闭''':局部变量线程私有 * '''ThreadLocal''':为每个线程创建独立副本 == 实际应用案例 == === 电商库存扣减 === <mermaid> sequenceDiagram 用户A->>服务端: 请求扣减库存(当前库存=1) 用户B->>服务端: 同时请求扣减同一商品 服务端->>数据库: 查询库存 数据库-->>服务端: 返回库存=1 服务端->>服务端: 并行执行扣减逻辑 服务端->>数据库: 两次update库存=0 </mermaid> '''解决方案''':使用数据库悲观锁或分布式锁保证原子性。 == 深入原理 == === Happens-Before规则 === Java内存模型定义的保证可见性的规则,包括: * 程序顺序规则 * 锁规则(解锁先于后续加锁) * `volatile`变量规则 === 指令重排序 === 编译器/处理器可能优化指令顺序,通过`volatile`或`synchronized`禁止重排序: <math> \begin{cases} \text{原顺序} & \text{load A → load B → store C} \\ \text{重排序} & \text{load B → load A → store C} \end{cases} </math> == 常见误区 == * 认为`volatile`能替代`synchronized`(实际仅保证可见性) * 忽略复合操作的非原子性(如`check-then-act`) * 过度同步导致性能下降 == 最佳实践 == 1. 优先使用不可变对象(如`String`) 2. 缩小同步代码块范围 3. 使用线程安全集合(如`ConcurrentHashMap`) 4. 避免在同步块中调用外部方法 {{编程概念页尾}} [[Category:计算机科学]] [[Category:面试技巧]] [[Category:Java并发编程]]
摘要:
请注意,所有对代码酷的贡献均被视为依照知识共享署名-非商业性使用-相同方式共享发表(详情请见
代码酷:著作权
)。如果您不希望您的文字作品被随意编辑和分发传播,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)
该页面使用的模板:
模板:编程概念导航
(
编辑
)
模板:编程概念页尾
(
编辑
)