synchronized 原理及JDK1.6后的优化_(1)

JVM中的同步基于监视器对象monitor实现,每个对象实例都有一个monitor,线程通过mutexLock实现互斥访问。当线程调用wait方法会释放mutex进入等待集合,而synchronized方法通过ACC_synchronized标志实现同步。线程在EntryList和waitSet中会阻塞,导致用户态和内核态切换的性能开销。自旋锁能减少不必要的阻塞,适合于短时间持有的锁,在多处理器系统中提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      JVM中得同步是基于进入与退出监视器对象(管程对象monitor),来实现的,每个对象实例都会有一个monitor对象,monitor对象会和Java对象一同创建并销毁。monitor对象是由C++实现的。

      当多个线程同时访问一段同步代码时,这些线程会被放到一个EntryList中,处于阻塞状态的线程都会被放到列表当中,接下来,当线程获取到对象monitor时,monitor是依赖于操作系统的mutex Lock来实现互斥,线程获取mutex成功,则会持有该mutex,这时其他线程就无法再获取到该mutex.

       如果线程调用了wait方法,那么线程就会被释放掉所持有的mutex,并且该线程会进入到waitSet集合(等待集合中),等待下一次被其他线程调用notify/notifyAll唤醒。如果当前线程顺利执行完毕方法,那么他也会释放掉所持有的mutex。

      对于synchronized关键字修饰的方法来说,并没有出现monitor与monitorexit指令,而是出现了一个Acc_synchronized标志。

     JVM使用了ACC_synchronized访问标志来区分一个方法是否为同步方法;当方法被调用时,调用指令会检查该方法是否持有Acc_synchronized标志,如果有,那么执行线程将会先持有方法所在对象的monitor对象,然后再去执行放方法体;在该方法执行期间,其他任何线程均无法再获取到这个Monitor对象,当线程执行完毕后,他会释放掉这个Monitor对象。

      实例:

      publicd synchronized void method(){}

        同步锁在这种实现方式当中,因为monitor是依赖于底层的操作系统实现的,这样就存在与用户态和内核态之间的切换,所以会增加性能的开销。

       通过对象互斥锁的概念来保证共享数据操作的完整性,每个对象都对应一个可称为(互斥锁)标记,这个标记用于保证在任何时刻,只能一个线程访问该对象。

      实现同步的同时引入新问题?

      那些处于EntryList与waitSet中的线程均处于阻塞状态,阻塞操作是由操作系统来完成的,在linux下是通过pthread_lock函数来实现的。线程阻塞便会进入到内核调度状态,这会导致系统在用户态和内核态来回切换,严重影响锁的性能。

      解决上述问题的办法便是自选(spin),其原理是:当发生Monitor的争用时,若owner能够在很短的时间内释放掉锁,则那些正在争用的线程就可以稍微等待一下(即所谓的自旋),在owner线程释放之后,争用线程可能会立刻获取到锁,从而避免了系统的阻塞。不过,当owner运行的时间超过了临界值之后,争用线程自选一段时间后依然无法获取到锁,这时争用线程则会停止自选进入到阻塞状态。所以,总体的思想是:先自选--->不成功,则再进入阻塞状态,尽量降低阻塞的可能性,对那些执行时间很短的代码块来说极大的性能提升。显然自旋在多处理器(多核)上才有意义。

<think>我们正在讨论Javasynchronized关键字的底层实现原理。根据用户的问题,需要结合站内引用内容进行回答。同时,根据系统指令,我们需要使用LaTeX格式表示数学表达式,并且引用站内内容时在末尾添加引用标识。 回顾站内引用: [^1]: 提到synchronized通过锁机制确保互斥访问,并提到JVM底层实现锁机制。 [^2]: 提到synchronized通过加锁和释放锁保证原子性,使代码串行执行。 [^3]: 提到JDK1.6后对synchronized进行了优化,引入了轻量级锁和偏向锁,这些锁可以不依赖Monitor操作。 [^4]: 提到synchronized的作用是防止多个线程同时执行,避免竞态条件和数据不一致。 用户的问题:理解Java synchronized关键字的底层实现原理。 回答结构: 1. 先介绍synchronized的基本作用和目标(互斥、原子性、可见性)。 2. 解释synchronized在字节码层面的实现(monitorenter和monitorexit指令,以及ACC_SYNCHRONIZED标志)。 3. 描述monitor机制(包括对象头中的Mark Word,以及Monitor对象的结构)。 4. 详细说明锁升级过程(偏向锁、轻量级锁、重量级锁)。 5. 总结synchronized的底层实现原理。 注意:在回答中适当引用站内内容,并在引用处标注[^1]、[^2]等。 另外,生成相关问题。 开始回答:</think>### Java synchronized 关键字的底层实现机制 在 Java 中,`synchronized` 关键字的底层实现依赖于 **JVM 的锁机制**和 **对象监视器(Monitor)**。其核心原理是通过锁的获取与释放实现线程互斥访问,并通过锁升级策略优化性能。以下是分步解析: --- #### 1. **字节码层面的实现** 当使用 `synchronized` 时,编译器会生成特定的字节码指令: - **修饰代码块**:插入 `monitorenter` 和 `monitorexit` 指令。 ```java public void syncMethod() { synchronized(obj) { // 编译后:monitorenter // 临界区代码 } // 编译后:monitorexit } ``` - `monitorenter`:尝试获取对象的监视器锁(Monitor Lock)。 - `monitorexit`:释放锁,确保锁在正常或异常退出时都能被释放[^1][^4]。 - **修饰方法**:在方法访问标志中添加 `ACC_SYNCHRONIZED`。 ```java public synchronized void syncMethod() { // 方法体 } ``` - 线程执行方法前自动获取锁,执行完毕自动释放锁[^1][^3]。 --- #### 2. **Monitor 机制的核心组成** 每个 Java 对象(包括类实例和 Class 对象)内部都关联一个 **Monitor 对象**,其结构如下: - **Monitor Lock(锁标志位)**:存储在对象头的 Mark Word 中,标识锁状态。 - **Entry Set(入口集)**:阻塞等待锁的线程队列。 - **Wait Set(等待集)**:通过 `wait()` 主动释放锁并进入等待的线程队列[^1][^4]。 当线程执行 `monitorenter` 时: 1. 检查锁标志位是否为无锁状态(`0`)。 2. 若为无锁状态,通过 CAS 操作获取锁,并将锁标志位改为偏向锁或轻量级锁。 3. 若锁已被占用,线程进入 Entry Set 阻塞等待[^1][^4]。 --- #### 3. **锁升级优化JDK 1.6+)** 为解决早期重量级锁的性能问题,JVM 引入了锁升级策略: | 锁阶段 | 实现原理 | 适用场景 | |--------------|-----------------------------------|------------------------------| | **偏向锁** | 在 Mark Word 记录线程 ID | 单线程重复访问同步块 | | **轻量级锁** | 通过 CAS 自旋尝试获取锁 | 多线程竞争不激烈 | | **重量级锁** | 线程阻塞并交由 OS 管理 | 高竞争场景 | - **升级流程**: 1. 首次加锁时启用偏向锁(记录线程 ID)。 2. 当其他线程竞争时,升级为轻量级锁(CAS 自旋 $10$-$20$ 次)。 3. 若自旋失败,膨胀为重量级锁(线程阻塞,进入 Entry Set 等待)[^3][^4]。 > 数学表达:锁升级过程中,设 $T$ 为线程竞争时间,$C$ 为 CAS 操作耗时,则轻量级锁的优化条件是 $T < C \times n$($n$ 为自旋次数上限)。 --- #### 4. **原子性与内存可见性的保证** - **原子性**:通过锁的互斥性,确保临界区代码串行执行(如 `a++` 操作)[^2]。 - **内存可见性**: - 锁释放时强制将工作内存数据刷新到主内存。 - 锁获取时强制从主内存读取最新数据,避免 CPU 缓存不一致[^1][^4]。 --- #### 5. **性能影响与优化建议** - 早期重量级锁涉及 OS 内核态切换,开销较大($\approx 10\mu s$)。 - 优化后偏向锁/轻量级锁开销降至 $\approx 1\mu s$[^3]。 - **建议**: - 减少同步块范围(如同步代码块而非方法)。 - 避免锁嵌套(防止死锁)。 - 高竞争场景改用 `java.util.concurrent` 锁(如 `ReentrantLock`)。 --- ### 总结 `synchronized` 的底层实现是 **JVM Monitor 机制 + 锁升级策略**: 1. 通过字节码指令 `monitorenter`/`monitorexit` 或 `ACC_SYNCHRONIZED` 触发锁操作。 2. 依赖对象头的 Mark Word 管理锁状态。 3. 基于竞争动态升级锁(偏向锁 → 轻量级锁 → 重量级锁)[^1][^3][^4]。 ```mermaid graph LR A[synchronized代码] --> B{锁类型} B -->|单线程| C[偏向锁] B -->|低竞争| D[轻量级锁-CAS自旋] B -->|高竞争| E[重量级锁-线程阻塞] ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值