从一个小故事理解锁升级(无锁、偏向锁、轻量级锁、重量级锁)

一、例子

  • 实验室管理员(张主任):JVM
  • 你:线程A
  • 研究员小方:线程B
  • 实验室:对象
  • 实验室的防盗锁:重量级锁
  • 雨伞:轻量级锁
  • 粉笔签名:偏向锁

💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖

👀👀👀你和小方是大学里的研究员,有次老师安排了一个实验室给你和小方同学用,但是实验室只能一个同学做实验,而且每次you或者小方用实验室的时候,都会将实验室打扫。(原子性,即存在竞态条件)。

由于,两个同学都不想自己在实验还没做完之前,数据被另一位同学清除。所以,他们就想办法如何互斥的使用这个实验室。

最开始,实验室用的是防盗锁。如果实验做完了,就把要是放在门框下,如果实验没做完(当你去吃饭,或者有其他事情的时候),锁住门,把钥匙带走。这样,就算离开了,小方也不能进去做实验,实验室的工作就是安全的。(Monitor重量级锁方式进行同步

🤷‍♂️🎈🐱‍🏍但是,很多情况下,发现小方都不怎么来实验室。一打听,才发现,小方喜欢夜里做实验,而你喜欢白天。你们实验的时间基本上是错开的。知道这件事之后,发现每次上锁,太麻烦了,有没有什么更加简单的方法?

一天,你找到小方商量了一下,

👀👀👀: “要不,我们门就不要锁了吧?谁用实验室,谁就把雨伞放在实验室门口?”

你和小方是一对好基友,甚至雨伞都是一样的,只能打开雨伞才能分辨出是自己的还是小方的。

这样,你白天去做实验的时候,就从以前需要开门的操作变成了,每次打开雨伞,看看伞是谁的,如果是自己的,直接进门做实验。如果不是自己的,就自己在门外等(轻量级锁)。并约定,这样子,我们还是用防盗锁吧,我看你白天也会来做实验(锁升级)。

🎨🐱‍🚀后来,小方回家了,很长一段时间,小方应该都不会用这个实验室了。你还是每次进实验室,打开雨伞检查,是不是自己的,虽然比以前上锁的方式简单,但是,还是很麻烦。

干脆,你就直接用粉笔在门口写上了自己的标识:xxx在做实验。下次要是自己还来的时候,发现自己的字没有被擦掉,说明没人想用这个实验室。如果,期间有人想用实验室,那么使用者就会将粉笔字擦掉,用挂雨伞的方式来保持互斥。(偏向锁升级为轻量级锁)🐱‍🐉😃

二、锁升级

JDK1.6之前,synchronized 只有一种重量锁,效率低下。JDK1.6之后,JVM的设计为了提高锁的获取与释放,从不同的应用场景对synchronized的上锁方式进行了优化,引入了偏向锁和轻量级锁的概念。

后面,锁的状态就出现了四种:无锁轻量级锁偏向锁重量级锁

四种状态会随着几个线程临界区的执行情况,锁会不断的进行升级,一次次的家中锁的力度。而且这个升级过程不可逆。

锁的级别由低到高依次是:无锁——>轻量级锁——>偏向锁——>重量级锁

2.1 无锁

无锁就是值没有对资源进行锁定,所有的线程都能更新同一个资源。但是,每次修改只会有一个成功。显著的特点是线程会不断的尝试修改共享资源,如果没有冲突就修改成功并退出,否则继续通过循环的方式尝试

如果多线程修改同一个值,那么一定会有一个线程能修改成功,而其他失败的会不断重试,一直到成功。

2.2 偏向锁

🐱‍💻😘🤣开始执行到synchronized代码块的时候,锁对象默认是编程偏向锁的(操作是通过CAS修改对象头里的锁标志)。通俗理解就是:偏向于第一个获得它的线程”的锁。执行完同步代码块后,线程并不会主动释放偏向锁。当第二次到达同步代码块时,线程会判断此时持有锁的线程是否就是自己(持有锁的线程ID也在对象头里),如果是则正常往下执行。由于之前没有释放锁,这里也就不需要重新加锁。如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外开销,性能极高。✔🎶❤

偏向锁是指当一段同步代码一直被同一个线程所访问时,即不存在多个线程的竞争时,那么该线程在后续访问时便会自动获得锁,从而降低获取锁带来的消耗,即提高性能。

当一个线程访问同步代码块并获取锁时,会在 Mark Word 里存储锁偏向的线程 ID。在线程进入和退出同步块时不再通过 CAS 操作来加锁和解锁,而是检测Mark Word里是否存储着指向当前线程的偏向锁。轻量级锁的获取及释放依赖多次 CAS 原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次 CAS 原子指令即可。

🎉✌偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。关于偏向锁的撤销,需要等待全局安全点,即在某个时间点上没有字节码正在执行时,它会先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,🐱‍🐉并撤销偏向锁,恢复到无锁(标志位为01)或轻量级锁(标志位为00)的状态。

2.3 轻量级锁

轻量级锁的使用场景:如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以 使用轻量级锁来优化。

轻量级锁是指当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能

在轻量级锁状态下继续锁竞争,没有抢到锁的线程将自旋,即不停地循环判断锁是否能够被成功获取。

获取轻量级锁有两种情况:

  • 偏向锁没有开启
  • 多个线程竞争偏向锁,有第二个线程加入锁竞争,偏向锁就升级为轻量级锁(自旋锁)。

锁竞争:如果多个线程轮流获取一个锁,但是每次获取锁的时候都很顺利,没有发生阻塞,那么就不存在锁竞争。只有当某线程尝试获取锁的时候,发现该锁已经被占用,只能等待其释放,这才发生了锁竞争

2.4 重量级锁

如果在尝试加轻量级锁的过程中,🧨CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有 竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。

当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。简言之,就是所有的控制权都交给了操作系统,由操作系统来负责线程间的调度和线程的状态变更。而这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,从而消耗大量的系统资.😢🤷‍♀️🤦‍♀️

在这里插入图片描述

三、优缺点对比

在这里插入图片描述
🥉🥇🥈🏅🏆

参考:

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值