synchronized底层原理

synchronized同步锁在加锁和解锁的过程中是依赖操作系统的互斥锁实现的。

synchronized的使用方式有三种:

  • 作用在实例方法上。修饰实例方法,相当于对当前实例对象this加锁,this作为对象监视器。
  • 修饰代码块。指定加锁对象,对给定对象加锁,括号括起来的对象就是对象监视器。
  • 修饰静态方法。修饰静态方法,相当于对当前类的Class对象加锁,当前类的Class对象作为对象监视器。

在多个线程并发对同一个数据进行操作时,不能保证原子性操作,操作可能会被另一个线程的操作覆盖,所有使用synchronized锁。 

synchronized代码块上是使用monitorenter/monitorexit实现,sychronized是使用对象内部的监视器来实现的,线程通过执行monitorenter尝试获取monitor的所有权,当monitor被所占用时就会处于锁定状态。

获取monitor所有权的过程:

如果monitord的进入数为0,则将进入数设置为1,该线程就是锁的持有者;如果monitor 的进入数为1,线程只是重新进入,那么进入数加1,如果进入数大于0,说明该锁被其他线程持有,该线程只能进入阻塞状态,直到进入数为0才能进入线程。

JVM底层在实现锁的过程中有三种1类型的锁(Java6后):偏向锁、轻量级锁、重量级锁。

偏向锁

使用场景:单线程

偏向锁只需要在切换线程设置ThreadID时,执行一次CAS原子指令,JVM会利用原子操作在对象头的Mark Word部分设置ThreadID以表示这个对象偏向于当前线程。

如果有另外一个线程试图锁定已经倾斜果的对象,那么就会切换为轻量级锁。

轻量级锁

使用场景:单线程交替执行

偏向锁和轻量级锁在使用上的区别:如果有一个方法需要使用到锁,者个方法从始至终都是一个线程调用,没有出现并发的情况,那么使用偏向锁就可以,第二次直接进入就可以,不用再进行加锁。

 锁的状态时保存在对象头中的,一个Java对象的存储结构,再内存中的存储布局分为3个区域:对象头(Header)、实例数据(Instsnce Data)、对齐填充(Padding).

轻量级锁加锁过程

1、在代码进入同步块的时候,如果对象锁转台为无锁状态(lock标志位为01) ,虚拟机首先将在当前线程的栈帧中建立一个名为Lock Record 的空间,用于存储锁对象目前的Mark Word拷贝。

2、拷贝对象头中的Mark Wor复制到锁的Lock Record中。

3、拷贝成功后,虚拟机尝试将对象的Mark Record中的ptr_to_lock_record更新指向为Lock Record的指针,并将Lock Record里的owner指针指向对象的Mark Word,如果更新成功,那么线程就拥有了该对象的锁,并且对象Mark Word的lock标志位为“00”,g该对象使用轻量级锁;如果更新失败,虚拟机先会检查对象的Mark Word是否已经指向了当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,就可以直接进入,如果不是,就说明有多个线程同时竞争该锁,就要将锁升级为重量级锁。

轻量级锁的解锁过程:

1.通过CAS指令,尝试把线程中的Mark Word对象替换成当前的Mark Word,如果替换成功,则同步过程完成,如果替换失败,说明多个线程同时获取该锁,要升级为重量级锁,

重量级锁

使用场景:多线程(比较消耗性能)

使用操作系统进行加锁和解锁需要频繁的进行用户态和内核态之间的切换,所以进行了锁的升级。

总结:偏向锁适用于单线程,轻量级锁适用于 单线程交替执行,重量级锁适用于多线程并发执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值