synchronized轻量级锁的自旋之谜:Java为何在临界区“空转“等待?

从餐厅等位理解自旋锁的智慧

想象两家不同的餐厅:

  • 传统餐厅:没座位时顾客去逛街(线程挂起,上下文切换)
  • 网红餐厅:没座位时顾客在门口短时间徘徊(线程自旋,避免切换)

Java的轻量级锁正是采用了类似网红餐厅的"自旋等待"策略,但背后的机制远比这更精妙!

轻量级锁的四大核心特性

1. 适用场景:低竞争短耗时

synchronized(lock) {  // 当没有线程竞争或竞争很小时
    // 执行时间很短的操作(纳秒~微秒级)
}

2. 实现原理:CAS+栈帧锁记录

// 伪代码示意轻量级锁获取
void enterLightweightLock() {
    LockRecord lr = new LockRecord(); // 在当前线程栈创建记录
    lr.displacedHeader = swapObjectHeader(MARK_WORD); // CAS替换对象头
    if (替换成功) {
        return; // 获取轻量级锁成功
    } else if (可重入) {
        lr.displacedHeader = null; // 重入计数
        return;
    }
    // 否则升级为重量级锁或自旋
}

3. 自旋策略:自适应决策

// JVM根据历史记录动态决定:
while (!tryLock()) {
    if (自旋次数超过阈值 || 竞争加剧) {
        升级为重量级锁;
        break;
    }
    短暂空转(通常10-100次循环);
}

4. 锁升级路径

在这里插入图片描述

自旋等待的5个关键问题

1. 为什么要自旋?

  • 避免上下文切换:挂起/唤醒线程的成本≈微秒级
  • 利用CPU空闲周期:等待期间CPU并非完全闲置

2. 自旋多久合适?

// OpenJDK的默认策略(版本不同有差异):
- 单核CPU:直接挂起(自旋无意义)
- 多核CPU:
  - 初始自旋次数:10次(-XX:PreBlockSpin- 后续自适应调整(-XX:+UseSpinning

3. 自旋会浪费CPU吗?

  • 短期自旋:消耗的CPU周期小于上下文切换开销
  • 长期竞争:超过阈值(约自旋10次)后升级为重量级锁

4. 如何观察自旋?

# 查看JVM编译日志(需添加参数)
-XX:+UnlockDiagnosticVMOptions 
-XX:+PrintAssembly 
-XX:+LogCompilation

5. 自旋失败会怎样?

// 当自旋超过阈值或竞争加剧:
1. 撤销轻量级锁
2. 膨胀为重量级锁(ObjectMonitor3. 线程进入等待队列

性能对比实验

操作类型无竞争(纳秒)轻量级锁+自旋(纳秒)重量级锁(纳秒)
加锁/解锁5-1020-501000+
短临界区(100ns)-1201500
长临界区(1ms)-升级为重量级锁2000

测试环境:4核CPU,JDK17

3大优化参数

1. 关闭自旋(不推荐)

-XX:-UseSpinning  # 强制直接使用重量级锁

2. 调整自旋次数

-XX:PreBlockSpin=20  # 默认10次(需配合-XX:+UseSpinning)

3. 启用偏斜锁(先决条件)

-XX:+UseBiasedLocking  # JDK15后默认禁用

代码示例:触发轻量级锁自旋

public class SpinLockDemo {
    static final Object lock = new Object();
    static volatile boolean ready = false;
    
    public static void main(String[] args) {
        // 竞争线程
        new Thread(() -> {
            synchronized (lock) {
                ready = true;
                while (true); // 持有锁不释放
            }
        }).start();
        
        // 自旋等待线程
        while (!ready); // 等待竞争线程先获取锁
        long start = System.nanoTime();
        synchronized (lock) {  // 这里会触发轻量级锁自旋
            System.out.println("获取锁耗时: " + (System.nanoTime()-start) + "ns");
        }
    }
}

现代JVM的智能决策

JDK15+的锁策略选择流程:
在这里插入图片描述

在这里插入图片描述

一句话总结

synchronized的轻量级锁就像聪明的"短跑选手"——在竞争不激烈时通过短暂自旋避免昂贵的上下文切换,这种权衡正是JVM优化艺术的完美体现! 🏃♂️⚡

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农技术栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值