JVM源码-synchronized锁升级过程
2022-03-09 18:07:40 12 举报
JVM源码-synchronized锁升级过程
作者其他创作
大纲/内容
否
是否是匿名偏向锁?
通过CAS尝试将Displaced Mark Word替换回对象头(锁状态变为无锁态)
获得偏向锁Thread Id | epoch | age |101
无锁状态?Neutral(无锁状态)mark->is_neutral()
是轻量级锁
成功
_owner指向的是当前的线程?cur == Self?说明是Monitor锁重入
是,直接返回ObjectMonitor
线程的重入次数,初次进入0
LorkRecord设置displaced_header=null,用于重入的计数。
成功,return
ObjectWaiter * volatile
hashCode
一个Java锁对象对应一个ObjectMonitor,因此同一时间只有一个线程获取膨胀权限
ArrayLength
BasicLock _lock; // _lock包含对象头指针
Object Mark Word
displaced mark word是执行原MarkWord的指针,但原MarkWord会被修改为重量级锁状态
1
epoch是否过期?
101偏向锁状态
失败
重量级锁ObjectMonitor
失败进入下次循环
01
开始释放轻量级锁CASE(_monitorexit) 和ObjectSynchronizer::fast_exit
是
偏向的是否是当前线程
_cxq的头结点为空?
成功或失败,均走这个分支
epoch过期?
Klass Pointer
inflate锁膨胀过程: 是一个死循环
epoch
当前锁状态为无锁状态?表示需要升级为轻量级锁
访问同步块
是,表示是偏向锁
到达全局安全点
本线程走轻量级锁加锁逻辑
bitfields
重量级锁释放结束
继续下一次循环
开始通过操作系统Park/Unpark竞争重量级锁(ObjectMonitor::EnterI)
设置Monitor_recursions++
找到首个空闲/当前线程的LockRecord
判断线程1是否还在同步代码块中
1) 新建INFLATING (全0)状态的MarkWord;2)CAS替换锁MarkWord属性
无锁
开始CAS竞争重量级锁(enter)
开始获取轻量级锁
1) 基于原MarkWord创建匿名偏向MarkWord2) 基于匿名偏向的MarkWord创建新MarkWord指向当前线程并拷贝锁对象Hash值3) 以匿名偏向MarkWord为原值CAS替换锁MarkWord
00
开始释放重量级锁(exit)
age
hash
将_cxq导入到EntryList中
其他线程正在膨胀中?INFLATING(膨胀中)mark == markOopDesc::INFLATING()
1) 新建MarkWord,设置为重量级锁状态,指向ObjectMonitor2) 直接替换锁MarkWord
关联锁对象指针
return
_object;
_owner为当前线程的LockRecord?Self->is_lock_owned ((address)cur)?说明由轻量级锁膨胀且第一次调用enter方法
CAS成功
1) 拷贝锁对象头中的MarkWord到LorkRecord中;2) 用LorkRecord CAS替换锁的MarkWord;
轻量级锁
字段
开启
存活
通过park挂起当前线程(一直阻塞)
未过期
将锁设置为无锁状态unbiased_prototype
未找到,从头再来
对齐填充(Padding)
CAS将monitor的_owner字段设置为当前线程
实例数据(Instance Data)
_header;
prototype_heade是否是偏向模式
不是
竞争获得重量级锁Monitor指针 | 10
失败,说明其他线程已经抢到了锁,直接退出
创建ObjectMonitor成功Monitor指针 | 10
准备访问同步块
设置Monitor_recursions = 1;_owner = Self;
死循环-必须成功当前线程包装成ObjectWaiter,状态设置成TS_CXQ,并加入cxq队首
拿到了锁执行同步代码块
将当前线程的node从cxq或EntryList中移除
未成功
不是重入
死循环
_owner;
线程2
指向当前锁记录的指针ptr to lock record
在入队之前,再进行一次自适应自旋TrySpin(Self)
执行同步代码块
CAS操作替换Thread Id
是重入
是,尝试重偏向
_EntryList的头结点不为空?
1) 创建无锁态MarkWord并设置到LockRecord中;2) 用LorkRecord CAS替换MarkWord;
升级为轻量级锁
会先自旋,超过1000次后yield,yield一定次数后执行park
用于标记GC
_WaitSet;
开始获取偏向锁(fast_enter,revoke_and_rebias)
_recursions != 0?表示锁重入的释放
_EntryList;
获得轻量级锁LockRecord指针 | 00
LorkRecord的displaced_header==null?
10
是匿名偏向锁
CAS修改锁为无偏向状态
_owner指向的LockRecord在当前线程栈上?表示当前线程持有的是轻量级锁,由轻量级锁膨胀而来。
1) 查找原持有锁线程的首次加锁的LorkRecord;设置属性displaced_header为unbiased_prototype(无锁态);2) 使用LorkRecord CAS替换锁MarkWord;(隐式转轻量级锁状态)
检查原持有偏向锁的线程状态
park返回后
创建ObjectMonitor_header=displaced mark word_owner=null
MarkWord中指针指向LockRecord中DisplacedMarkWord?表示是轻量级锁
0(是否偏向锁)
创建ObjectMonitor_header=displaced mark word_owner=LockRecord
无锁状态
为空
最近进入被阻塞的ObjectWaiter链表
CAS失败
否,不是锁重入
oop _obj; // 锁对象指针
_recursions--;
开始创建ObjectMonitor(inflate)
调用TryLock(Self)和TrySpin(Self)获取锁
如果已经持有重量级锁?Inflated(重量级锁状态)mark->has_monitor()
JVM 锁膨胀升级 - 偏向锁升级轻量级锁逻辑
// LockRecord在openjdk中通过两个类BasicObjectLock和BasicLock来实现class BasicObjectLock VALUE_OBJ_CLASS_SPEC {font color=\"#b71c1c\
先释放锁,这时如果有其他线程进入同步块则能获得锁
volatile intptr_t
非偏向锁状态
11
释放锁流程
未开启
1) 基于原MarkWord创建无锁状态MarkWord2) 基于原MarKWord创建新MarkWord并放置到LockRecord中3) 以无锁状态MarkWord为原值用LockRecord CAS替换锁MarkWord(隐式转换轻量级锁)
当前锁状态
当前锁偏向当前线程?
偏向锁阶段
锁对象
走到这里,说明_cxq和_EntryList都为空
被唤醒
开始获取偏向锁
对象头
成功返回Monitor
BasicObjectLock
遍历ThreadsList判断线程1是否存活
不是当前线程ID
设置 _owner = THREAD;_recursions = 0;
可GC
ObjectMonitor
为轻量级锁mark->has_locker()?且对应LorkRecord在当前的线程栈中?表示轻量级锁重入
ObjectMonitor指针
只需比较Thread Id
锁对应Klass关闭了偏向锁模式
偏向锁释放结束
匿名偏向状态
是,表示锁重入释放,直接退出
否,上面情况都不满足,说明锁要么偏向别的线程,要么是匿名偏向
执行同步当中的逻辑
依赖mutex(操作系统的互斥)
指向重量级锁monitor的指针ptr to heavyweight monitor
MarkWord:
volatile markOop
1) 新建MarkWord,设置为重量级锁状态,指向ObjectMonitor2) CAS替换锁MarkWord
1) 新建MarkWord,设置为重量级锁状态,指向ObjectMonitor2)CAS替换锁MarkWord属性
解释器运行时阶段(interpreterRuntime.hpp)(InterpreterRuntime::monitorenter)
关联锁对象头指针
不是,表示是重量级锁
1(是否偏向锁)
调用wait方法等待的ObjectWaiter链表
不为空
成功,表示释放成功
是否开启偏向锁(-XX:+UseBiasedLocking)
_recursions;
CAS替换MarkWord中Thread Id
要执行之后的操作需要重新获得锁,即CAS设置_owner为当前线程
开始释放偏向锁(CASE(_monitorexit))
占用当前锁的线程
获得偏向锁Thread Id | epoch| age |101
不成功,说明解锁时发生了竞争
成功,说明获得了Monitor,return
线程1
轻量级锁LockRecord
开始获取轻量级锁(slow_enter)
获得偏向锁Thread Id|epoch| age |101
void* volatile
不是锁重入
LockRecord指针
否,不是当前线程
_cxq;
是,直接退出
锁状态是偏向模式?
获得锁成功
当其他线程释放锁时,会根据唤醒策略,从cxq或EntryList中挑选一个线程unpark唤醒
偏向锁
没有等待的线程或已经有假定继承人?
1) 新建MarkWord,设置为重量级锁状态,指向ObjectMonitor2) 直接替换锁MarkWord属性
将对象头MarkWord中线程ID指向自己
_owner为null?表示无锁状态
_owner不是当前线程?
-
0
因为执行了批量撤销到达上限,表明已经不再持有锁了1) 基于Klass的prototype_header创建新MarkWord并拷贝锁对象Hash值(无锁状态)2) CAS替换锁MarkWord
注意: lock在CASE(_monitorenter)时已经创建好了, 并且其obj指针已经指向了锁对象
1) 基于Klass的prototype_header创建新MarkWord指向当前线程并拷贝锁对象Hash值2) CAS替换锁MarkWord
轻量级锁释放结束
对象是否是匿名偏向状态(101)
标志位(2bit)
从链表头遍历查找持有此锁对象的LockRecord,释放LockRecord
此时锁状态: 轻量级锁状态
还在同步代码块中
void * volatile
查找线程1的首次加锁LockRecord;设置属性displaced_header为unbiased_prototype(无锁态);MarkWord指向LockRecord;
参与竞争的ObjectWaiter链表
检查锁对象MarkWord中线程ID是否是当前线程ID
暂停线程1
重量级锁
撤销偏向锁流程开始
加锁流程
Klass的prototype_header是为偏向模式?表示class未关闭偏向模式
同步对象Object
线程访问同步代码块
TryLock (Self)尝试获取锁
Mark Word
同步代码块执行结束
失败,此时锁状态: 无锁态
特征
CAS一次失败即升级
是轻量级锁?Stack-locked(轻量级锁状态)mark->has_locker()
thread id
开始偏向锁撤销(revoke_bias)
解释器阶段bytecodeInterpreter.cpp(CASE(_monitorenter))
属性
状态
失败,继续下次循环
不再存活 或已退出同步代码块
LorkRecord在当前的线程栈中?表示锁重入
在调用系统的同步操作之前,先尝试自适应自旋获得锁TrySpin(Self)。
0 条评论
下一页