深入理解Java虚拟机-线程安全与锁优化

线程安全级别

不可变(Immutable)

对象状态不可变,天然线程安全。

String

Integer

绝对线程安全

所有操作都线程安全(Java 中极少见)。

Vector

(通过同步实现,但复合操作仍不安全)

相对线程安全

单次操作线程安全,复合操作需同步。

Collections.synchronizedList()

线程兼容

需调用方通过同步保证安全。

ArrayList

线程对立

无论是否同步都无法保证安全(设计错误)。

未正确同步的共享变量

线程安全的实现方式

互斥同步(Mutual Exclusion)

  • 核心机制:通过锁(如 synchronized、ReentrantLock)保证临界区代码的原子性
  • 底层原理:synchronized:基于对象头中的 Mark Word 和 Monitor 锁实现锁状态:无锁 → 偏向锁 → 轻量级锁 → 重量级锁ReentrantLock:基于 AQS(AbstractQueuedSynchronizer) 实现,支持公平/非公平锁

非阻塞同步(Non-Blocking)

  • 核心机制:通过 CAS(Compare-And-Swap) 实现无锁编程
  • CAS 指令AtomicIntegerAtomicReference 等类的底层实现
  • 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),直接阻塞竞争线程
  • 适用场景:多线程高竞争(如秒杀场景)
全部评论

相关推荐

评论
1
2
分享

创作者周榜

更多
牛客网
牛客企业服务