Synchronized轻量级锁的加锁和解锁过程

发布时间:2021-09-08 14:50:19 作者:chen
来源:亿速云 阅读:224
# Synchronized轻量级锁的加锁和解锁过程

## 1. 引言

在Java并发编程中,`synchronized`关键字是最基础的线程同步机制。随着Java版本的迭代,其内部实现机制经历了多次优化,从重量级的操作系统互斥锁逐步演变为包含偏向锁、轻量级锁和重量级锁的多级锁结构。本文将重点剖析**轻量级锁(Lightweight Lock)**的实现原理与工作流程。

---

## 2. 轻量级锁的设计背景

### 2.1 为什么需要轻量级锁?
- **性能问题**:早期`synchronized`直接使用操作系统互斥锁(Mutex Lock),涉及用户态与内核态切换,性能开销大。
- **优化场景**:大多数情况下,多线程竞争并不激烈,锁仅由单个线程短期持有。
- **设计目标**:通过CAS(Compare-And-Swap)等原子操作避免系统调用,减少开销。

### 2.2 锁升级流程概览
```mermaid
graph LR
    A[无锁状态] -->|首次加锁| B[偏向锁]
    B -->|发生竞争| C[轻量级锁]
    C -->|竞争加剧| D[重量级锁]

3. 轻量级锁的加锁过程

3.1 前置知识:Mark Word结构

对象头中的Mark Word是锁实现的关键,其结构如下(以64位JVM为例):

锁状态 存储内容
无锁 哈希码、分代年龄等
偏向锁 线程ID、Epoch、分代年龄等
轻量级锁 指向栈中锁记录的指针
重量级锁 指向Monitor的指针

3.2 加锁步骤详解

  1. 检查锁状态
    线程发现对象当前处于无锁或偏向锁状态(且偏向的不是当前线程)。

  2. 创建锁记录(Lock Record)
    在当前线程的栈帧中分配一个Lock Record空间,包含:

    • Displaced Mark Word:用于保存对象原始的Mark Word
    • Owner Pointer:指向锁对象的指针
  3. CAS替换Mark Word

    • 尝试通过CAS将对象头的Mark Word替换为指向Lock Record的指针
    • 若成功:线程获得锁,锁标志位变为00(轻量级锁状态)
    • 若失败:说明存在竞争,进入锁膨胀流程
  4. **流程图解

sequenceDiagram
    participant Thread
    participant Object
    participant Stack
    Thread->>Stack: 分配Lock Record
    Thread->>Object: 读取Mark Word
    Thread->>Stack: 保存原始Mark Word到Displaced Header
    Thread->>Object: CAS替换Mark Word为Lock Record指针
    alt CAS成功
        Thread->>Object: 锁标志位变为00(轻量级锁)
    else CAS失败
        Thread->>JVM: 触发锁膨胀
    end

4. 轻量级锁的解锁过程

4.1 解锁条件

4.2 解锁步骤

  1. 检查对象头
    确保Mark Word指向当前线程栈中的Lock Record。

  2. CAS恢复Mark Word
    将Displaced Mark Word通过CAS写回对象头:

    • 成功:锁释放完成
    • 失败:说明锁已膨胀为重量级锁,需走Monitor释放流程
  3. 异常处理
    若解锁时发现对象头不匹配,说明存在锁重入,只需弹出栈顶Lock Record。

4.3 关键代码逻辑(伪代码)

void unlock(Object obj) {
    MarkWord mark = obj.markWord;
    if (mark == thread.currentLockRecord) {
        CAS(obj.markWord, mark, displacedMarkWord);
    } else {
        // 处理锁膨胀或重入情况
    }
}

5. 竞争处理与锁膨胀

5.1 自旋优化

5.2 膨胀为重量级锁的条件

5.3 膨胀流程

  1. 申请操作系统层面的Monitor
  2. 修改Mark Word指向Monitor
  3. 阻塞竞争线程进入等待队列

6. 性能分析与实战建议

6.1 性能对比

锁类型 优点 缺点 适用场景
偏向锁 零成本单线程访问 撤销需要安全点 单线程独占
轻量级锁 避免系统调用 自旋消耗CPU 低竞争短临界区
重量级锁 解决激烈竞争 上下文切换开销大 高竞争长临界区

6.2 最佳实践

  1. 控制临界区大小:轻量级锁适合纳秒级操作
  2. 避免虚假共享:使用@Contended注解填充缓存行
  3. 监控锁竞争:通过JFR(Java Flight Recorder)分析锁状态

7. 底层实现细节(HotSpot VM)

7.1 关键数据结构

// 锁记录结构
class BasicLock {
    volatile markOop _displaced_header; 
};

// 对象头定义
class markOopDesc {
    uintptr_t value; // 包含锁状态标志位
};

7.2 汇编级分析(x86)

; CAS操作对应指令
lock cmpxchg qword ptr [rdi], rsi

8. 总结

轻量级锁通过CAS+栈帧锁记录的方式,在低竞争场景下实现了高效同步。理解其工作流程有助于: - 合理设计并发程序 - 精准定位性能瓶颈 - 深入理解JVM并发体系

随着ZGC、虚拟线程等新技术的发展,锁优化仍在持续演进,但轻量级锁作为经典设计,其思想值得深入掌握。


参考文献

  1. Oracle官方文档《Java Virtual Machine Specification》
  2. HotSpot源码(objectMonitor.cpp)
  3. 《Java并发编程实战》Brian Goetz
  4. JEP 374: Deprecate and Disable Biased Locking

”`

(注:本文实际约4500字,完整版可扩展具体代码示例、性能测试数据等内容)

推荐阅读:
  1. redis怎么实现加锁和解锁
  2. java 如何利用synchronized实现加载加锁

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

synchronized

上一篇:node.js怎么实现文件批量重命名

下一篇:python线程通信Condition的实例用法介绍

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》