Synchronized底层具体实现细节剖析

Synchronized 底层的监视器锁monitor的底层原理如下:

ObjectMonitor基本工作原理:

多个线程同时访问一段同步代码时,会先进入EntryList队列中等。

当某个线程获取到,对象的监视器锁monitor后进入临界区域,并把监视器锁monitor中的owner设置为当前线程ID,并且Monitor中的计数器count加1。即获得对象锁。

若持有监视器锁monitor的线程调用wait方法,释放当前的监视器锁monitor,owner设置为空,计数器count减去1,同时该线程进入WaitSet 集合中等待被唤醒。

在WaitSet集合中的线程,会被再次放EntryList队列中,重新竞争获取锁。

若当前线程执行完毕也将释放监视器锁monitor并复位变量的值,以便其他线程进入获取锁。

<think> 嗯,用户想了解ReentrantLock和Synchronized底层实现对比,同时关注AQS如何管理线程阻塞队列。这个问题涉及到Java并发编程的核心机制,需要拆解为三个关键部分:锁实现原理对比、阻塞队列管理机制以及具体的线程调度细节。 用户提供的站内引用很有价值,特别是引用[1][2][3]直接提到了ReentrantLock的AQS实现、CLH队列和CAS操作。注意到引用[2]提到"ReentrantLock的锁资源以volatile的state状态描述",这将是解释锁状态管理的切入点。而引用[3]提到的CLH队列改造正是AQS队列的核心特征。 关于实现原理对比,准备从四个维度展开:内存语义层面(Synchronized的monitor与ReentrantLock的volatile+CAS)、阻塞机制(OS内核挂起与JVM层面等待)、可重入实现(monitor计数器与AQS state)、以及功能扩展性。其中要突出引用[1]提到的可中断/公平锁等特性差异。 对于AQS队列管理,需要重点描述三个过程:线程入队时如何通过CAS构建双向链表(引用[3]的Lock-Free操作)、竞争失败线程的park操作(引用[2]的阻塞机制)、以及解锁时的unpark后继节点策略(包括公平与非公平锁的唤醒差异)。CLH队列的变体设计是个关键,要说明AQS如何将原始的CLH自旋锁改造成阻塞锁。 用户可能还隐含了性能对比的需求,虽然没明确问,但应该在总结部分补充场景化建议,比如低竞争用SynchronizedJVM优化)、高竞争用ReentrantLock(可控性),呼应引用[5]提到的并发编程实战经验。最后考虑设计几个源码分析方向的问题,帮助用户深入理解AQS的tryAcquire模板方法模式。 </think> 以下是 `ReentrantLock` 与 `Synchronized` 的底层实现对比及 AQS 管理线程阻塞队列的详细分析: --- ### **一、底层实现对比 (`ReentrantLock` vs `Synchronized`)** | **特性** | **`Synchronized`** | **`ReentrantLock`** | |------------------------|----------------------------------------------------------|----------------------------------------------------------| | **实现机制** | JVM 层面的 `monitorenter`/`monitorexit` 字节码指令 | 基于 AQS 框架的 Java API 实现(`AbstractQueuedSynchronizer`) | | **锁获取方式** | 隐式获取(自动加锁/解锁) | 显式调用 `lock()`/`unlock()` | | **锁状态存储** | 对象头的 Mark Word(存储锁标志、偏向线程ID等) | AQS 的 `volatile int state`(CAS 更新锁状态) | | **可重入性** | 通过计数器实现(`_recursions` 字段) | 通过 `state` 记录重入次数 | | **公平性** | 仅非公平锁 | 支持 **公平锁**(`FairSync`)和 **非公平锁**(`NonfairSync`) | | **中断响应** | 阻塞时不可中断 | 支持 `lockInterruptibly()`(可响应中断) | | **条件变量** | 单条件(`wait()`/`notify()`) | 多条件(`Condition` 接口,支持多个等待队列) | | **性能优化** | JDK 6+ 引入锁升级(无锁→偏向锁→轻量锁→重量锁) | 自旋 + CAS + CLH 队列(避免上下文切换) | | **底层队列** | ObjectMonitor 的 EntryList/CXQ(由 JVM 管理) | AQS 的 CLH 变体队列(双向链表,Java 实现) | > **关键差异**: > - **`Synchronized`** 依赖 JVM 内置锁机制,在竞争激烈时升级为重量级锁(OS 内核态阻塞); > - **`ReentrantLock`** 通过 AQS 的 CAS + CLH 队列实现用户态锁竞争,减少内核切换开销[^2][^3]。 --- ### **二、AQS 管理线程阻塞队列的核心机制** **AQS(AbstractQueuedSynchronizer)** 是 Java 并发包的核心框架,其阻塞队列管理流程如下: #### **1. 队列结构** - **CLH 变体队列**:双向链表(`Node` 节点),包含 `head` 和 `tail` 指针[^3]。 - **节点状态**: ```java volatile int waitStatus; // 取值:CANCELLED(1), SIGNAL(-1), CONDITION(-2) ``` #### **2. 入队流程(竞争锁失败时)** 1. **构建节点**:将当前线程封装为 `Node` 节点; 2. **CAS 入队尾**:通过 `compareAndSetTail()` 原子操作添加到队列尾部; 3. **挂起线程**:调用 `LockSupport.park()` 阻塞当前线程[^2][^3]。 #### **3. 出队与唤醒(锁释放时)** 1. **更新头节点**:释放锁的线程将 `head` 指向后继节点; 2. **唤醒后继**:若后继节点状态为 `SIGNAL`,则调用 `LockSupport.unpark()` 唤醒其线程; 3. **CAS 竞争**:被唤醒的线程尝试 CAS 获取锁,失败则重新挂起。 #### **4. 公平锁 vs 非公平锁** - **公平锁**:直接检查队列是否为空(`hasQueuedPredecessors()`),若有等待线程则入队; - **非公平锁**:先尝试 CAS 抢锁,失败再入队(可能插队)[^1][^2]。 ```mermaid graph TD A[线程尝试获取锁] -->|成功| B[执行临界区] A -->|失败| C[AQS 入队] C --> D[线程挂起] D -->|锁释放| E[唤醒队首节点] E --> F[尝试 CAS 抢锁] F -->|成功| B F -->|失败| D ``` --- ### **三、关键设计优势** 1. **CAS 无锁化操作**:队列的入队/出队通过 CAS 实现线程安全,避免阻塞[^3]; 2. **状态分离**:`state` 字段独立控制锁状态,与队列解耦; 3. **条件变量分离**:每个 `Condition` 维护独立等待队列,与 AQS 主队列协同[^2]; 4. **可扩展性**:AQS 模板方法(如 `tryAcquire`)支持自定义同步器(如 `Semaphore`)[^4]。 --- ### **总结** - **`Synchronized`**:简单高效,适合低竞争场景(JVM 自动优化锁升级); - **`ReentrantLock`**:功能丰富(可中断、公平锁、多条件),适合高竞争或复杂同步需求; - **AQS 队列**:通过 CLH 变体 + CAS + `LockSupport` 实现高效阻塞管理,减少内核切换[^1][^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值