Markword格式
无锁
首先每个实例对象都有一个对象头,对象头中有一个MarkWord记录着对象的hashcode
、年龄以及锁信息
下面是无锁状态的Markword格式
hashcode | unused | age | 0 | 01
倒数第三位的0代表没有偏向
偏向锁
Markword格式
threadid | epoch | age | bias |1| 01
倒数第三位的1代表有偏向
偏向锁可以怎么理解呢,偏向就是说这个锁偏袒一个线程, 但只有一个线程的时候使用,它的速度是优于轻量级锁的,因为轻量级锁设计到CAS操作,而偏向锁只需要线程获取对象锁的时候第一次CAS就行。
偏向锁和轻量级锁都适合没有资源竞争的情况,(如果由资源竞争就会发生锁升级)
偏向锁默认情况下是开启的,也可以由UseBiasLocking开关来控制
有几种情况会发生撤销偏向
- 当另一个线程B获取锁的时候,发现线程id不对,就会撤销偏向,将偏向锁转为无锁状态,然后线程B偏向锁,用完之后又变为线程A的偏向,该动作称为撤销偏向
- 当调用对象的hashCode方法的时候也会撤销偏向,为啥呢,因为偏向锁没有地方可以存hashCode,对象的Markword用来存线程id等内容了。
- 当调用wait方法、notify方法的时候会撤销偏向,为啥呢,因为wait和notify适用于重量级锁
偏向锁的优化
- 当撤销偏向发生20次后,虚拟机就会认为该对象的偏向线程不正确,对这些对象进行批量重偏向。
- 当撤销偏向发生40次后,虚拟机就会批量撤销,自此就关闭了偏向锁,所有初始化的对象都为001的无锁状态。
轻量锁
markword格式
ptr_to_lock_record | 00
轻量锁通过CAS和锁记录实现,每个线程都有独立的栈帧,但所对应方法去获取对象的锁时,就会通过CAS比较并交换Markword和锁记录的内容,有三种情况。
交换成功:交换域其实是我自己方便理解定义的东西,就是如果上锁成功,交换对象头中markword的内容
锁重入:同一个线程的方法,栈帧中的锁记录指向对象
交换失败:说明存在竞争,锁升级为重量级锁。
重量级锁
markword格式
prt_to_monitor | 10
重量级锁是基于monitor实现的,monitorenter 临界区 monitorexit这样的方式
第一个获得锁的为Owner,后续竞争锁的进入entryset排序,如果由wait方法阻塞的线程进入waitset等待唤醒。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RuUTNK2T-1690200978978)(attachment:7a51cf586f503c023d32b2ab2b644555)]
自旋锁
就是说进行的线程不会立刻进入entrySet,会现在cpu里面自旋等待一段时间,否则的化又要io浪费时间,但是这种方式也会浪费cpu的时间,适合多核cpu。JVM会对自选设置自定义的阈值,默认10,由JVM去优化,到底自旋多少次合适。
锁消除
不存在对象逃逸的情况下,且没有临界资源,某个线程独占式的访问
锁粗化
同一个线程,同一个临界区,重复进入临界区多次,粗化为进入一次