synchronized实现原理及锁升级过程

synchronized实现原理及锁升级过程

 

前言:

synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的

尽管最初synchronized的性能效率比较差,但是随着版本的升级,synchronized已经变得原来越强大了,本文带大家了解的是synchronized实现原理及锁升级过程,希望可以帮助到大家。

1.用法

synchronized可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。

synchronized有三种应用方式:

作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁;

作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁;

作用于代码块,对括号里配置的对象加锁。

2.实现原理

2.1 Java对象头

synchronized用的锁存在Java对象头里,Java对象头里的Mark Word默认存储对象的HashCode、分代年龄和锁标记位。在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。32位JVM的Mark Word可能变化存储为以下5种数据:

面试必问---synchronized实现原理及锁升级过程你懂吗?

 

锁一共有四种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态随着竞争情况逐渐升级。为了提高获得锁和释放锁的效率,锁可以升级但不能降级,意味着偏向锁升级为轻量级锁后不能降级为偏向锁。

1.偏向锁

当一个线程访问同步块并获取锁时,会在对象头和栈帧的锁记录里存储偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需测试Mark Word里线程ID是否为当前线程。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要判断偏向锁的标识。如果标识被设置为0(表示当前是无锁状态),则使用CAS竞争锁;如果标识设置成1(表示当前是偏向锁状态),则尝试使用CAS将对象头的偏向锁指向当前线程,触发偏向锁的撤销。偏向锁只有在竞争出现才会释放锁。当其他线程尝试竞争偏向锁时,程序到达全局安全点后(没有正在执行的代码),它会查看Java对象头中记录的线程是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程,撤销偏向锁,升级为轻量级锁,如果线程1不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

2.轻量级锁

线程在执行同步块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头的MarkWord复制到锁记录中,即Displaced Mark Word。然后线程会尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁。如果失败,表示其他线程在竞争锁,当前线程使用自旋来获取锁。当自旋次数达到一定次数时,锁就会升级为重量级锁。

轻量级锁解锁时,会使用CAS操作将Displaced Mark Word替换回到对象头,如果成功,表示没有竞争发生。如果失败,表示当前锁存在竞争,锁已经被升级为重量级锁,则会释放锁并唤醒等待的线程。

流程大致如下:

面试必问---synchronized实现原理及锁升级过程你懂吗?

 

结束:

今天就分享到这里,有不对需要改进的地方还望大佬们指出

### Java 中 `synchronized` 关键字的实现原理 `synchronized` 是一种用于控制多个线程对共享资源访问的关键字,在 Java 编程语言中被广泛应用于解决并发编程中的数据竞争问题。当一个方法或一段代码块被声明为 `synchronized` 后,意味着同一时刻只有一个线程可以执行该部分代码。 具体来说,`synchronized` 的工作原理依赖于对象内部的一个监视器(Monitor)。每当有线程要进入被标记为 `synchronized` 的区域时,都需要先获取这个;而一旦离开此区域,则会释放掉持有的[^1]。 对于同步方法而言,如果是一个实例方法,那么就是当前对象本身 (`this`);如果是静态方法的话,则是对应类的 Class 对象。通过这种方式来确保任何时间点上最多只有一个线程能持有特定对象上的并执行相应的操作。 ### 升级机制 为了提高性能表现,JVM 设计了一套复杂的优化策略——即所谓的“升级”。这一过程大致分为以下几个阶段: #### 轻量级 (Lightweight Locking) 轻量级定是在没有争用的情况下采用的一种高效加协议。在这种模式下,每个尝试获得的操作都会直接检查目标对象头内的标志位,以此判断是否有其他活动线程正在占用这把。如果没有发现冲突,则可以直接设置自己的标识完成快速占有动作而不必陷入重量级的竞争状态。 #### 偏向 (Biased Locking) 偏向是针对大多数情况下只会在单一线程间传递的特点设计出来的进一步简化方案。首次成功请求到某对象之后,不仅会给其打上已定标签还会记录下最初拥有者的身份信息。此后只要还是同一个线程再次试图重新获取相同位置处的就可以免去重复验证流程从而加快速度。 #### 重量级 (Heavyweight Locking) 只有当以上两种较为温和的方式都无法满足需求时才会真正进入到传统意义上的完全互斥等待队列里排队等候解通知。此时涉及到操作系统层面的支持,通常伴随着较高的上下文切换开销成本。 随着应用负载的变化情况,上述三种形式之间会发生动态转换:从无 -> 偏向 -> 轻量级 -> 最终演变为重量级过程被称为“膨胀”。 ### Java 内存模型(JMM) 和可见性保障 Java 内存模型规定了 JVM 如何处理多线程环境下的内存读写行为,并提供了必要的抽象层次使得开发者不必关心具体的硬件架构差异。其中一个重要特性便是保证了线程之间的 **可见性** :即某个线程所做的更改应当及时反映给其它可能观察这些变化的地方。 在涉及 `synchronized` 的场景中,每次退出临界区之前都要刷新本地缓存至主存之中,同时也会阻止编译期及运行时期内可能出现的危害程序逻辑正确性的指令重排序现象发生。因此即使存在不同的 CPU 核心或是处理器架构也能维持一致的行为特征[^2]。 ```java public class Counter { private int count; public synchronized void increment() { // 加入同步修饰符 this.count++; } public synchronized int getCount() { // 获取最新值也需要同步保护 return this.count; } } ```
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值