synchronize锁定过程
2022-04-24 20:25:14 0 举报
Java编程语言synchronize关键字锁定流程
作者其他创作
大纲/内容
no
分配和初始化监视器对象
markOop mark=object->mark();对象标记位5个状态:Inflated:已经膨胀为重量级锁;Stack-locked:对象处于轻量级锁状态,竞争加剧直接强制膨胀为重量级锁;Inflating:膨胀中,等待膨胀完成;Neutral:无锁对象,竞争则直接膨胀为重量级锁;Baised:偏向锁
计算出预期偏向锁值anticipated_bias_locking_value(用于判断有没有批量撤销偏向锁)
没有偏向锁:批量重偏向/批量撤销
更新ObjectMoniter的_owner属性
检查线程suspend标志位是否为false?
判断对象标记位状态是否为重量级锁?
没有锁竞争情况下,偏向锁的获取流程
非线程安全点:调用BiasedLocking进行撤销和偏向
设置监视器对象的头部;设置拥有监视器的对象等;设置头部监视器位为监视器地址
对象是否处于无锁状态?
头部替换位轻量级锁脂针
线程获取到监视器的锁,断开链接(从监视器的entrylist或者_cxq队列断开链接)
批量撤销偏向锁后需要更改epoch值,如果epoch改变了,当前线程需要尝试CAS重偏向
调用moniterenter
yes
获取监视器对象返回(第一个步骤完成)
正常未上锁对象
BasicObjectLock保存锁对象
通过CAS操作,修改对象头部标记位Inflating正在膨胀中状态
其他偏向锁持有偏向锁发生冲突,撤销偏向锁(CAS将对象头从mark替换为原始头部),获取偏向锁失败
尝试获取锁,成功?
CAS重偏向是否成功?
获取锁对象
获取偏向锁的步骤:一、没有锁竞争情况下:1、获取一个空闲的Lock Recod(即BasicObjectLock,每个线程都有一个Lock Record记录)2、将Lock Record的oop指针指向当前的锁对象lock3、获取锁对象的对象头mark word判断是否可偏向(锁标记为101)且线程ID为空(1)如果可偏向,且偏向线程为当前线程则不需要再抢占(2)不可偏向状态:通过轻量级锁完成锁的抢占过程4、如果锁对象lock偏向其他线程或者当前是匿名偏向状态(也就是没有偏向任何一个线程)则先构建一个匿名偏向的Mark Word,然后通过CAS方法,把匿名偏向的Mark Word修改为偏向线程,如果当前锁对象lock已经偏向了其他线程那么CAS一定会失败。二、存在锁竞争情况下:偏向锁已经偏向了一个线程;这个时候新来的线程访问同一个对象锁触发锁竞争并触发偏向锁撤销流程1、新线程调用撤销偏向锁方法,尝试撤销lock锁对象的偏向锁;2、撤销偏向锁需要到达全局安全点才会执行(全局安全点即当前线程运行到这个位置,线程状态可确定,堆对象状态可确定,在这个位置的JVM可以进行GC,偏向锁撤销等动作。到达全局安全点后会暂停获得偏向锁的线程;3、判断获得偏向锁线程的状态:(1)已经执行完同步块代码或者处于非存活状态,直接把偏向锁恢复成无锁状态,然后新线程升级到轻量级锁,通过轻量级锁抢占锁(2)还在执行同步块代码(没有退出同步块代码),直接把lock锁对象升级成轻量级锁并指向当前持有偏向锁的线程,接着该线程继续执行同步块内的代码。
anticipated_bias_locking_value==0?(有没有批量撤销偏向锁?)
yes
当前线程是否位对象的轻量级锁拥有者?
当前线程是否在线程安全点?
存在竞争情况下的偏向锁的获取
CAS失败说明发生了竞争
CAS替换是否失败?
是否只撤销当前对象的偏向锁?
持有监视器锁的线程是不是当前线程?
尝试自旋,成功?
成功撤销
对象是否上锁且当前线程是否为锁的占有者?
CAS操作当前对象头替换为原始头部
CAS操作是否成功?
尝试CAS获取轻量级锁(CAS更新mark)
线程栈找到一个空闲的BasicObjectLock对象
获取当前对象的元数据klass对象
进入死循环(等待竞争)
设置等待监视器为null(即成功获取锁)
开启偏向锁,但是偏向锁状态为匿名偏向,尝试重偏向到当前线程
等待优化
锁膨胀的四种情况
获取锁对象的头部标记信息markOop
进入死循环
线程阻塞
更新唤醒次数等
自旋竞争逻辑:1、如果开启固定自旋,则进行固定旋转2、预自旋,统计相关自旋数据3、判断是否空旋转4、判断是否超过旋转最大线程数5、自旋
是否操作成功?
偏向线程为当前线程且epoch是否变化?
直接返回
锁膨胀分为两个步骤:1、获取监视器对象;2、锁对象头部指向监视器对象(监视器对象上锁)
判断对象标记位是否处于轻量级锁状态?
CAS重偏向
更新Java线程状态表明阻塞在moniter enter处
CAS替换是否成功?
进入快速模式(避免不必要的锁膨胀)
触发锁膨胀
判断是否轻量级锁重入?
CAS将线程对象替换到ObjectMoniter监视器对象_owner上
可能是批量撤销偏向锁
正常偏向锁?
CAS替换对象头部为监视器对象地址
线程自我阻塞直到被其他线程唤醒
epoch_mask_in_place值是否不等于0?(当前线程是否需要重偏向?)
for continue
尝试自旋竞争
更新线程suspend状态(即暂停线程)
klass对象没有偏向锁则通过CAS操作替换klass对象头部为原始对象头部
Java :synchronized(ock){}编译后的字节码:moniterenter //同步块开始 //源代码文件ByteCodeInterpreter.cppxxxxxxxxxmoniterexit //同步块结束xxxreturn
锁膨胀的第二个步骤:ObjectMoniter上锁
成功,成功获取偏向锁,直接退出
调用VMThread撤销偏向锁
每个线程都有一个Lock Record列表(即BasicObjectLock列表);每一个Lock Record关联一个锁对象的Mark WordBasicObjectLock类的属性:private BasicLock lock;private Oop oop;//指向lock锁对象的指针BasicLock类的属性:private volatile markOop_displaced_header;//用来保存锁对象lock的原始Mark Word
enter进入监视器对象内部操作ObjectMoniter上锁过程
继续判断是否有其他线程持有偏向锁?
CAS成功与否?
是匿名偏向锁并且不尝试重偏向?
是否直接使用重量级锁?
结束
尝试获取轻量级锁
设置lock对象的替换头部为null
多个线程在互相争用这个监视器
锁膨胀的第一个步骤:获取监视器对象
释放监视器对象
是否开启偏向锁?
启发式计算
轻量级锁的获取:1、将锁对象的Mark Word拷贝至Lock Record(BasicObjectLock)内2、通过CAS操作修改锁对象的Mark Word指向Lock Record(BaiscObjectLock对象)设计思路:锁对象在竞争过程中有可能发生变化,但是每个线程的Lock Record的Mark Record不受影响。因此当出发膨胀的时候,能够通过Lock Record和锁对象的Mark Word进行比较判定在持有轻量级锁的过程中,锁对象是否被其他线程抢占过,如果有则在轻量级锁释放过程中唤醒被阻塞的其他线程
线程安全点:撤销偏向锁
是否使用偏向锁?
继续重试
自旋失败,进入监视器等待队列中等待竞争
成功膨胀为重量级锁(成功获取重量级锁)
是否成功获得锁?
尝试获取锁成功?
判断对象标记位是否为正在膨胀中?
CAS尝试替换对象头部为lock对象地址
成功获取轻量级锁
进入慢速模式(不使用偏向锁)
ObjectMonitor对象字段_owner:保存当前持有锁的线程;_object:保持所对象的指针;_cxq:存储没有获得锁的线程队列,是一个链表结构;_WaitSet:当调用Object.wait()方法阻塞时,被阻塞的线程会保存到该队列中;_recursions:记录重入次数:
创建ObjectWatier节点,自旋CAS头插法插入_cxq队列
等待竞争
是否发生了重偏向?(epoch值是否发生了变化?)
释放监视器
被其他线程唤醒
klass对象有没有偏向锁?
是否直接返回(是否重偏向)
开始
重入
获取对象的标记头
没有批量撤销偏向锁,且当前线程已经持有偏向锁,成功获取偏向锁,直接退出
是否尝试重偏向?
锁膨胀的四种情况:1、重量级锁重入(已经是重量级锁);2、其他线程正在膨胀过程中,自旋直至完成锁膨胀;3、其他线程获取轻量级锁,当前锁进行膨胀4、无锁状态,其他线程刚释放了锁,当前线程需要完成锁膨胀
对象被其他线程上锁了(轻量级锁加锁失败)
偏向锁撤销
0 条评论
下一页