偏向锁
HotSpot
的作者
经过
研究
发现
,大多数情况下,
锁
不
仅
不存在多
线
程
竞
争,而且
总
是由同
一
线
程多次
获
得,
为
了
让线
程
获
得
锁
的代价更低而引入了偏向
锁
。当一个
线
程
访问
同步
块
并
获
取
锁时
,会在
对
象
头
和
栈帧
中的
锁记录
里存
储锁
偏向的
线
程
ID
,以后
该线
程在
进
入和退出
同步
块时
不需要
进
行
CAS
操作来加
锁
和解
锁
,只需
简单
地
测试
一下
对
象
头
的
Mark Word
里是否
存
储
着指向当前
线
程的偏向
锁
。如果
测试
成功,表示
线
程已
经获
得了
锁
。如果
测试
失
败
,
则
需
要再
测试
一下
Mark Word
中偏向
锁
的
标识
是否
设
置成
1
(表示当前是偏向
锁
):如果没有
设
置,
则
使用
CAS
竞
争
锁
;如果
设
置了,
则尝试
使用
CAS
将
对
象
头
的偏向
锁
指向当前
线
程。
(
1
)偏向
锁
的撤
销
偏向
锁
使用了一种等到
竞
争出
现
才
释
放
锁
的机制,所以当其他
线
程
尝试竞
争偏向
锁时
,
持有偏向
锁
的
线
程才会
释
放
锁
。偏向
锁
的撤
销
,需要等待全局安全点(在
这
个
时间
点上没有正
在
执
行的字
节码
)。它会首先
暂
停
拥
有偏向
锁
的
线
程,然后
检查
持有偏向
锁
的
线
程是否活着,
如果
线
程不
处
于活
动
状
态
,
则
将
对
象
头设
置成无
锁
状
态
;如果
线
程仍然活着,
拥
有偏向
锁
的
栈
会被
执
行,遍
历
偏向
对
象的
锁记录
,
栈
中的
锁记录
和
对
象
头
的
Mark Word
要么重新偏向于其他
线
程,要么恢复到无
锁
或者
标记对
象不适合作
为
偏向
锁
,最后
唤
醒
暂
停的
线
程。
图
2-1
中的
线
程
1
演示了偏向
锁
初始化的流程,
线
程
2
演示了偏向
锁
撤
销
的流程。
(
2
)关
闭
偏向
锁
偏向
锁
在
Java 6
和
Java 7
里是默
认
启用的,但是它在
应
用程序启
动
几秒
钟
之后才激活,如 有必要可以使用JVM
参数来关
闭
延
迟
:
-XX:BiasedLockingStartupDelay=0
。如果你确定
应
用程 序里所有的锁
通常情况下
处
于
竞
争状
态
,可以通
过
JVM
参数关
闭
偏向
锁
:
-XX:- UseBiasedLocking=false,那么程序默
认
会
进
入
轻
量
级锁
状
态
。
轻量级锁
(
1
)
轻
量
级锁
加
锁
线
程在
执
行同步
块
之前,
JVM
会先在当前
线
程的
栈桢
中
创
建用于存
储锁记录
的空
间
,并
将
对
象
头
中的
Mark Word
复制到
锁记录
中,官方称
为
Displaced Mark Word
。然后
线
程
尝试
使用
CAS
将
对
象
头
中的
Mark Word
替
换为
指向
锁记录
的指
针
。如果成功,当前
线
程
获
得
锁
,如果失
败
,表示其他
线
程
竞
争
锁
,当前
线
程便
尝试
使用自旋来
获
取
锁
。
(
2
)
轻
量
级锁
解
锁
轻
量
级
解
锁时
,会使用原子的
CAS
操作将
Displaced Mark Word
替
换
回到
对
象
头
,如果成
功,
则
表示没有
竞
争
发
生。如果失
败
,表示当前
锁
存在
竞
争,
锁
就会膨
胀
成重量
级锁
。
图
2-2
是
两个
线
程同
时
争
夺锁
,
导
致
锁
膨
胀
的流程
图
。
锁的优缺点对比