线程安全
约 578 字大约 2 分钟
2025-08-25
如何理解线程安全
线程安全的本质
线程安全的核心在于:当多个线程并发访问共享资源(变量、对象、文件等)时,程序的执行结果始终符合预期,且不会出现数据错乱或逻辑错误。其根源在于以下三个层面的问题:
- 原子性:操作是否不可分割,即一个操作要么完整执行,要么完全不执行。
- 可见性:一个线程对共享变量的修改能否立即对其他线程可见。
- 有序性:程序代码的执行顺序是否可能被编译器和 CPU 重排序。
i++ 操作的非原子性分析
以 i++ 操作为例,虽然表面是单行代码,但实际是非原子操作。在 JVM 中,i++ 被编译为四条字节码指令:
getstatic i // 读取静态变量 i 的值到操作数栈
iconst_1 // 将常量 1 压入操作数栈
iadd // 将栈顶两个值相加(i+1)
putstatic i // 将计算结果写回静态变量 i多线程环境下,线程切换可能发生在任意指令之间,导致操作被“切割”。例如:当线程 A 和线程 B 同时执行 i++(初始值 i=0):
- 线程 A 读取
i=0,线程 B 也读取i=0 - 线程 A 计算
i+1=1,线程 B 计算i+1=1 - 线程 A 写回
i=1,线程 B 写回i=1
最终结果:i=1(期望值为 2),出现数据错误。根本原因在于多线程的指令交叉执行破坏了原子性。
解决方案
针对线程安全问题,可通过以下机制保障原子性、可见性和有序性:
同步锁机制
synchronized:通过监视器锁(Monitor)实现代码块或方法的互斥访问。Lock接口(如ReentrantLock):提供更灵活的锁控制,支持超时和中断。
原子类
- 使用
java.util.concurrent.atomic包下的原子类(如AtomicInteger),通过 CAS(Compare-And-Swap)指令实现无锁原子操作。 - 例如:
AtomicInteger.incrementAndGet()可安全替代i++。
- 使用
注
根据场景选择方案。同步锁适用于复杂同步逻辑,原子类适用于简单计数器等高频场景。