1.6锁的升级过程



对象的HashCode是需要手动调用的,当没有调用时获取锁用的就是偏向锁,当调用完HashCode,就不在使用偏向锁,改为轻量级或者重量级锁,
wait方法会导致锁直接升级成重量级锁。

轻量级锁升级成重量级锁:
1、自旋锁尝试失败次数超过一定阈值(默认为5次),会升级为重量级锁。
2、多次尝试获取对象的锁,如果有其他线程尝试获取对象的锁,则当前线程会一直自旋等待,
直到获取到锁或者等待时间超过一定的阈值(默认为500个时钟周期),此时会升级为重量级锁。

小结:2个条件,自选5次失败,或者等待500个时钟周期

在这里插入图片描述

一、偏向锁

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

轻量级锁

当有新的线程进来时
在这里插入图片描述

在这里插入图片描述
其实就是竞争不激烈,但是确实存在多个锁竞争的情况,而且是,大家都很有序的进行,一释放,下一个线程就拿到锁,很有顺序的获取锁,基本上通过自旋的方式代替了Sync… 本质是自旋CAS完成。

在这里插入图片描述
那自旋CAS什么时候会升级成为重量级锁。

重量级锁

轻量级锁升级为重量级锁的步骤如下:

当一个线程尝试获取一个已经被另一个线程持有的轻量级锁时,它会进入自旋等待。在这个过程中,它会不断地检查锁是否被释放。
如果自旋等待超过了预设的次数(即自旋阈值),JVM会认为线程之间的竞争非常激烈,因此决定将轻量级锁升级为重量级锁。这个过程是通过膨胀(inflate)操作实现的。
膨胀操作会将轻量级锁的标记位改为重量级锁的标记位,并将当前持有锁的线程的ID存储在对象头中。这个过程需要CAS(Compare-And-Swap)操作来保证原子性。
一旦膨胀操作完成,其他尝试获取该锁的线程将被阻塞并放入锁的等待队列中。此时,锁的竞争已经变得非常激烈,因此需要使用更重的锁策略来确保线程安全。
重量级锁的释放过程相对复杂。当持有锁的线程释放锁时,它会唤醒等待队列中的一个线程来竞争该锁。这个过程涉及到线程的挂起和唤醒操作,因此开销较大。

1、调用在这里插入图片描述

在这里插入图片描述

调用完对象的HashCode方法后,这个对象将永远无法转成偏向锁。只能转成轻量级锁或者重量级锁,为了不能转成偏向锁,是因为HashCode生成了以后,在升级成偏向锁,没有地方存这个hashCode,因为官方的建议是hashCode只能生成一次。

或者说当线程获得偏向锁时,我们调用HashCode方法,锁会升级成了轻量级锁或者重量级锁。

在这里插入图片描述
轻量级锁时,栈的锁指针记录可以保存hashCode.

在这里插入图片描述

### ### synchronized 在 JDK 1.5 和 1.6 中的升级机制 在 JDK 1.5 中,`synchronized` 的实现仍然采用重量级机制,即所有同步操作都依赖操作系统层面的互斥量(mutex)来实现线程阻塞与唤醒。这种实现方式在多线程竞争激烈的情况下性能较差,因为每次的获取和释放都需要进行系统调用,导致较高的上下文切换开销。 到了 JDK 1.6,`synchronized` 的实现引入了升级机制,通过在 JVM 层面对状态进行分层管理,显著提升了性能。的状态包括无状态、偏向状态、轻量级状态和重量级状态,升级路径是不可逆的,即一旦升级到更高级别的,就不会再降级[^2]。 #### 无状态 对象刚被创建时处于无状态,此时没有任何线程持有。 #### 偏向状态 当一个线程第一次访问同步块时,JVM 会将对象头中的标志设置为偏向,并记录持有的线程 ID。这样,后续该线程再次进入同步块时无需进行同步操作,直接进入代码段。这种优化减少了无竞争情况下的同步开销[^4]。 #### 轻量级状态 当有第二个线程尝试获取时,偏向会失效,升级轻量级轻量级通过 CAS(Compare and Swap)操作尝试将对象头中的指针指向当前线程的栈帧,如果成功则获取,否则进入自旋等待。自旋等待的时间是有限的,如果在自旋过程被释放,则当前线程可以立即获取而无需阻塞[^4]。 #### 重量级状态 如果自旋次数超过一定阈值或等待线程较多,轻量级会膨胀为重量级,此时线程会被挂起并进入操作系统等待队列,等待的释放。这种机制避免了长时间自旋造成的资源浪费,但也带来了较高的上下文切换成本[^4]。 JDK 1.6 中还引入了自适应自旋(Adaptive Spinning),即根据前一次在同一个上的自旋等待结果来决定下一次是否自旋以及自旋的时间长短,从而提高自旋的成功率,减少不必要的阻塞[^3]。 这些优化策略使得 `synchronized` 在 JDK 1.6 中的性能得到了显著提升,缩小了与 `ReentrantLock` 的性能差距,甚至在某些场景下表现更优。 ```java public class SynchronizedExample { private int count = 0; public synchronized void increment() { count++; } public static void main(String[] args) throws InterruptedException { SynchronizedExample example = new SynchronizedExample(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final count: " + example.count); } } ``` 上述代码展示了 `synchronized` 的基本使用方式。在 JDK 1.6 中,该代码在执行过程中会经历升级过程,从无状态逐步升级到偏向轻量级,甚至可能升级重量级,具体取决于运行时的线程竞争情况。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

信仰_273993243

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

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

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

打赏作者

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

抵扣说明:

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

余额充值