概览
锁的实现基于对象头重的MarkWord,分为 无锁 -> 偏向锁 -> 轻量锁 -> 重量锁 四个状态
状态转换
锁的获取与释放
模型:
- Markword:指向Monitor的指针
- Monitor:_cxq,_EntryList,_WaitSet,owner,_recursions[重入数量],_Succ
重量级锁
- 场景:多线程竞争
- 获取:
- 线程T1尝试获取锁,if 当前_owner == NULL,则直接获取,_owner = Self,_recursions = 1; elif owner == Self,则_recursions++;
- 线程T1发现锁被占用,T1被封装成ObjectWaiter插入到cxq,
- 若T1是第一个等待线程,则会间歇性尝试获取锁park(interval):8秒
- 否则无限期挂起park()
- 释放:
- _recursions > 0,则_recursions–(减少一次重入)
- T1释放锁,根据不同的Knob_Qmode选择一个线程唤醒
- Qmode == 1,则从EntryList取一个线程,若EntryList为空,则将cxq倒置插入到EntryList,再取
- Qmode == 2,则从cxq重选择一个线程
- 被选择唤醒的线程,将尝试获取锁
轻量级锁
- 场景:线程间交替访问临界区(无竞争)
- 获取:
- 若无锁
- T1将对象Lockee的Markword存储到Lock Record的displaced header重
- 通过CAS尝试将LR的地址存储到Lockee的Markword,成功则表示T1获取了锁,否则进入锁膨胀逻辑
- 若是轻量级锁,判断是否为T1
- 是,表示重入,LR的displaced header设置为NULL,表示一次重复
- 否,进入锁膨胀逻辑
- 若无锁
- 释放:
- LR的displaced header设置为NULL
- 若对象的Markword只想LR,从LR的displaced header重将原Markword’恢复,释放锁
- 否则,进入锁膨胀逻辑
偏向锁
- 场景:仅一个线程访问过临界区
- 获取与撤销:
- T1检查锁对象,发现处于无锁,使用CAS将当前线程ID写入锁对象Markword,执行同步块内容
- T1重入,锁处于偏向状态且threadid就是T1,无需重复获取(无需CAS)
- T2访问临界区,锁对象处于偏向状态且非偏向自己,则在线程栈重新建一个Lock Record,用于存储对象的Markword,通过CAS尝试将 锁对象更新为LR地址
- 成功:拥有该锁,锁标志改为00(轻量锁)
- 失败:膨胀为重量锁