Java并发:偏向锁、轻量级锁、重量级锁

锁的状态

锁的状态一共有四种:无锁、偏向锁、轻量级锁、重量级锁

锁的状态保存在对象的头文件中,以32位JDK为例:

锁状态

25 bit

4bit

1bit

2bit

23bit

2bit

是否是偏向锁

锁标志位

轻量级锁

指向栈中锁记录的指针

00

重量级锁

指向互斥量(重量级锁)的指针

10

GC标记

11

偏向锁

线程ID

Epoch

对象分代年龄

1

01

无锁

对象的hashCode

对象分代年龄

0

01


锁的初始为无所状态,随着锁的竞争,锁的升级过程为: 偏向锁 ---> 轻量级锁--->重量级锁,锁的升级是单向的,不会出现锁的降级

锁升级过程

(初始:无锁状态)

场景一、线程#1 获取偏向锁 

    1. 线程#1 访问同步块

    2. 当前对象头,锁标志位:01,是否是偏向锁:0

    3. CAS更新Mark Word,设置偏向锁标志位1,设置线程ID为线程#1的ID

线程#1成功获取偏向锁,偏向锁获取成功后,线程不会主动释放,即使线程执行完毕

场景二、线程#1拥有偏向锁

    1. 线程#1 又要访问同步块

    2. 判断锁标志位为01,是偏向锁,判断线程ID是线程#1的ID

    3. 执行同步代码

场景三、线程#2 竞争偏向锁

    1. 发现当前对象头是偏向锁

    2. 判断线程ID是否为线程#2的ID:否

    3. 判断线程#1是否还存在,不存在--->场景四,存在--->场景五

场景四、线程#2 竞争偏向锁,线程#1 不存在,线程#2 获取偏向锁

    1. 设置偏向锁标志位为0

    2. 类似场景一,线程#2 获取偏向锁

场景五、线程#2 竞争偏向锁,线程#1 存在,偏向锁升级为轻量级锁

    1.  到达全局安全点(在这个时间点上没有字节码正在执行),线程#1挂起

    2. 设置锁标志位为00,将线程#1 的mark word复制到线程栈的lock record中,更新mark word,将mark word指向线程#1中monitor record的指针 (升级为轻量级锁)

    3. 继续执行线程#1 的代码

    4. 线程#2 自旋获取锁对象

场景六、线程#3竞争轻量级锁

    1. 拷贝对象头的mark word到线程栈的lock record中

    2. CAS操作将mark word更新为指向lock record的指针,并将Lock record里的owner指针指向object mark word,成功--->3,失败--->4

    3. 更新成功,线程#3则获取到了锁

    4. 更新失败,检查对象的mark word是否指向当前线程的栈帧,如果是则说明已经拥有该对象锁,直接进入同步快执行。否则轻量级锁膨胀为重量级锁,mark word中存储的是指向重量级锁(互斥量)的指针

轻量级锁CAS操作之前堆栈与对象的状态



轻量级锁CAS操作之后堆栈与对象的状态


重量级锁、轻量级锁和偏向锁之间的转换


总结

优点

缺点

适用场景

偏向锁

加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距。

如果线程间存在锁竞争,会带来额外的锁撤销的消耗。

只有一个线程执行同步块

轻量级锁

竞争的线程不会阻塞,提高了程序的响应速度。

如果始终得不到锁竞争的线程使用自旋会消耗CPU。

线程交替执行同步块

追求响应时间。

同步块执行速度非常快。

重量级锁

线程竞争不使用自旋,不会消耗CPU。

线程阻塞,响应时间缓慢。

追求吞吐量。

同步块执行速度较长。

参考文章:http://www.cnblogs.com/paddix/p/5405678.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值