深入理解Java虚拟机-线程安全与锁优化
线程安全级别
不可变(Immutable) | 对象状态不可变,天然线程安全。 |
、
|
绝对线程安全 | 所有操作都线程安全(Java 中极少见)。 |
(通过同步实现,但复合操作仍不安全) |
相对线程安全 | 单次操作线程安全,复合操作需同步。 |
|
线程兼容 | 需调用方通过同步保证安全。 |
|
线程对立 | 无论是否同步都无法保证安全(设计错误)。 | 未正确同步的共享变量 |
线程安全的实现方式
互斥同步(Mutual Exclusion)
- 核心机制:通过锁(如 synchronized、ReentrantLock)保证临界区代码的原子性
- 底层原理:synchronized:基于对象头中的 Mark Word 和 Monitor 锁实现锁状态:无锁 → 偏向锁 → 轻量级锁 → 重量级锁ReentrantLock:基于 AQS(AbstractQueuedSynchronizer) 实现,支持公平/非公平锁
非阻塞同步(Non-Blocking)
- 核心机制:通过 CAS(Compare-And-Swap) 实现无锁编程
- CAS 指令:
AtomicInteger
、AtomicReference
等类的底层实现 - ABA 问题:通过
AtomicStampedReference
解决 - 硬件支持:x86 架构的
cmpxchg
指令
锁优化策略
偏向锁(Biased Locking)
- 目的:减少无竞争场景下的同步开销
- 原理:记录首个获取锁的线程 ID,后续该线程无需 CAS 操作即可直接进入临界区
- 适用场景:单线程重复访问锁
- 参数:
-XX:+UseBiasedLocking
(默认开启)
轻量级锁(Lightweight Locking)
- 目的:减少多线程轻度竞争时的锁开销
- 原理:通过 CAS 操作将对象头替换为线程栈指针,避免操作系统级阻塞
- 适用场景:低并发竞争(如两个线程交替访问)
自旋锁(Spin Lock)
- 目的:减少线程阻塞和唤醒的上下文切换开销
- 原理:线程在竞争锁时循环等待(自旋),而非立即挂起
- 参数:
-XX:PreBlockSpin=10
(默认自旋 10 次后升级为阻塞)
锁消除(Lock Elimination)
- 目的:去除无实际竞争场景下的冗余锁
- 原理:JIT 编译器通过 逃逸分析,若发现锁对象仅被当前线程使用,直接删除锁操作
锁粗化(Lock Coarsening)
- 目的:减少频繁加锁/解锁的开销
- 原理:合并多个连续的锁操作为一个更大的锁范围
适应性自旋(Adaptive Spinning)
- 目的:动态调整自旋策略,平衡 CPU 资源消耗
- 原理:根据历史自旋成功率和锁持有时间,动态调整自旋次数或直接阻塞
重量级锁(Heavyweight Locking)
- 目的:解决高并发竞争下的线程安全
- 原理:依赖操作系统互斥量(mutex)和条件变量(condition variable),直接阻塞竞争线程
- 适用场景:多线程高竞争(如秒杀场景)