偏向锁、轻量级锁、重量级锁的理解和适用场景

对象头:synchronized用的锁是存在Java对象头里的。Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位,主要用来表示对象的线程锁状态。

栈帧:也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。

锁会随着线程的竞争情况逐渐升级,偏向锁 => 轻量级锁 => 重量级锁 。锁可以升级但是不能降级。升级的目的是为了提高获得锁和释放锁的效率。

偏向锁
当一个线程访问同步块获取锁时,会在对象头和栈帧的锁记录里存储锁偏向的线程ID,以后该线程再进入和推出同步块时不需要进行CAS(比较和交换,下次详细记录一下)操作来加锁和解锁。

通过判断对象头的Mark Word里面是否存有指向当前线程的偏向锁来决定是否需要使用CAS竞争锁。

适用场景:偏向锁只适合大部分锁没有被竞争的系统中,如果系统中存在大量被争用的锁时,会导致持有锁的线程不断切换,这时可以考虑关闭偏向锁。

可以通过JVM参数关闭偏向锁:

-XX:- UseBiasedLocking=false
,程序默认会进入轻量级锁状态。

轻量级锁
阻塞线程需要cpu从用户态转到内核态,代价比较大。而且可能会出现刚阻塞不久,锁就被释放的情况。为了缩小性能消耗,引入轻量级锁。

加锁

线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用 CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

扫描二维码关注公众号,回复: 16213386 查看本文章

解锁

轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

适用场景:少量线程交替获取锁,同步块执行速度非常快的场景。追求响应时间。

优缺点对比
偏向锁

优:加锁/解锁不需要额外的消耗

缺:有竞争时,会有额外的撤销锁或升级锁的消耗

轻量级锁

优:竞争的线程不会被阻塞(采用自旋),提高了程序的响应速度

缺:始终得不到锁的线程一直自旋会消耗cpu,造成cpu浪费(自旋好像就是无实际意义的循环,可以设定一个自旋等待的最大时间)

重量级锁

优:线程竞争不使用自旋,线程竞争锁失败后会阻塞,cpu的消耗会减少,增大了数据的吞吐量

缺:线程阻塞,响应速度慢

适用场景:大量线程同时竞争锁,追求吞吐量。


以下场景可异步化

1、打印日志
2、邮件发送 文件上传 定时任务同步数据
3、下发配置
4、部署服务
5、话费充值
6、微服务健康度扫描

猜你喜欢

转载自blog.csdn.net/m1195900241/article/details/126382244