sychronized 的自旋锁、偏向锁、轻量级锁、重量级锁,分别介绍和联系

📅2026/7/12 3:06:05 👁️次浏览
sychronized 的自旋锁、偏向锁、轻量级锁、重量级锁,分别介绍和联系
在 Java 中synchronized关键字底层的锁机制在 JDK 1.6 之后经历了重大的优化引入了“锁升级”Lock Escalation的概念 。你提到的这四种锁并不是完全独立的四种机制而是synchronized在不同竞争激烈程度下为了平衡性能开销而采取的不同状态或策略。这里需要先澄清一个核心概念偏向锁、轻量级锁、重量级锁是对象头Mark Word中记录的锁状态而自旋锁是一种在获取锁失败时为了避免线程立即阻塞而采用的优化技术。以下是它们各自的详细介绍以及相互之间的升级联系一、 核心概念分别介绍1. 偏向锁 (Biased Lock)核心思想假设一段同步代码在大多数情况下不仅不存在多线程竞争而且总是由同一个线程多次获得 。工作原理当线程第一次访问同步块并获取锁时会在对象头和栈帧的锁记录中存储该线程的 ID 。以后该线程再进入和退出同步块时只需检查对象头的线程 ID 是否一致无需再进行任何底层的 CAS (Compare-And-Swap) 同步操作 。适用场景极低并发场景或者基本上只有一个线程在跑同步块 。补充说明由于维护偏向锁在多线程竞争时撤销的成本较高JDK 15 之后已经默认废弃了偏向锁 。2. 轻量级锁 (Lightweight Lock)核心思想假设多线程会交替执行同步块即有多个线程但刚好错开时间没有发生真正的同时刻碰撞 。工作原理当偏向锁被其他线程访问时偏向模式被破坏锁会升级为轻量级锁 。线程在自己的栈帧中创建锁记录Lock Record然后通过 CAS 操作尝试将对象的 Mark Word 替换为指向自己锁记录的指针 。如果成功则获取锁 。适用场景线程交替执行同步块存在极短时间的微弱竞争 。3. 自旋锁 (Spin Lock) / 适应性自旋核心思想线程的阻塞和唤醒需要 CPU 从用户态切换到内核态开销非常大 。自旋锁认为“锁被占用的时间通常很短我不急着去睡觉阻塞我先在门外转几个圈空循环等一下” 。工作原理在轻量级锁竞争失败时线程不会立即挂起而是执行一个空的for循环自旋 。如果在自旋期间锁被释放了该线程就能直接拿到锁从而避免了昂贵的上下文切换 。适应性自旋JDK 1.6 引入自旋的次数不再固定 。如果上次自旋成功拿到了锁这次就多转几圈如果上次没拿到这次就少转甚至不转直接升级 。4. 重量级锁 (Heavyweight Lock)核心思想最传统的锁用于应对激烈的多线程碰撞 。工作原理依赖于操作系统的Mutex Lock互斥量来实现 。当锁升级为重量级锁后所有没有拿到锁的线程都会被真正地挂起阻塞进入等待队列放弃 CPU 调度直到持有锁的线程释放锁后将它们唤醒 。适用场景高并发、代码块执行时间较长、锁竞争非常激烈的场景 。二、 它们之间的联系锁膨胀/升级过程这四者是一个动态演进、单向升级的过程旨在让系统在不同并发压力下都保持最优性能 。整个升级链路如下无锁状态 - 偏向锁当对象刚被创建且第一个线程进入synchronized代码块时对象头的锁标志位变为偏向锁并记录下该线程的 ID 。此时没有任何额外的同步开销 。偏向锁 - 轻量级锁当第二个线程尝试获取该对象的锁时发现偏向锁的线程 ID 不是自己 。此时偏向锁会被撤销Revoke并在全局安全点Safe Point将锁状态升级为轻量级锁。这两个线程开始通过 CAS 操作竞争锁 。轻量级锁 自旋锁的结合当轻量级锁竞争出现时比如线程 A 还在执行线程 B 进来了线程 B 发现通过 CAS 拿不到锁 。此时线程 B不会立刻升级为重量级锁并阻塞而是启动自旋锁机制在 CPU 上空转等待一小段时间 。轻量级锁 - 重量级锁如果线程 B 自旋超过了规定的次数或者在 B 自旋的过程中又来了一个线程 C 参与竞争这说明当前的竞争非常激烈 。此时自旋不再有意义只会白白浪费 CPU 资源锁会发生最后一次膨胀升级为重量级锁。此时除了拿到锁的线程外其余所有竞争线程都会被操作系统放入阻塞队列进入睡眠状态 。总结synchronized默认先给你最轻的偏向锁如果有别人来凑热闹就升级为轻量级锁如果两人撞车了没抢到的人先用自旋锁原地等一等如果等不到或者来抢的人太多彻底撕破脸升级为需要操作系统介入的重量级锁。