Synchronized理论

Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。

  • 原子性:确保线程互斥的访问同步代码
  • 可见性:保证共享变量的修改能够及时可见
  • 有序性:有效解决重排序问题

请添加图片描述

偏向锁:

  • 当jvm开启偏向锁模式(默认),新创建的obj它的markword表示匿名偏向,处于这种状态的偏向锁,可以被任何线程竞争。竞争者使用CAS的方式将锁对象markword的高54bit设置为当前线程ID,成功则表示拿到了锁,否则就走锁升级逻辑。
  • 偏向锁退出锁时,并不会将markword再修改为"匿名偏向"状态.这样的好处是下一次偏向锁再次获取这把锁时,仅仅对比当前线程id是不是markword的偏向锁线程即可了。
  • 当偏向锁不是偏向当前线程状态的时候,或者CAS竞争失败,当前线程需要撤销偏向锁,将偏向锁升级。撤销偏向锁的时候,是在提交安全点执行偏向锁撤销任务。

撤销偏向锁为什么需要在安全点执行?
线程获取锁的第一步就是向线程栈的锁记录空间中push一条锁记录信息。并且每次重入,都会插入一条心的锁记录,用于表示重入次数。
锁记录空间原则上只允许本线程操作,所以一定是线程安全的。当偏向锁线程依然持有偏向锁时,说明偏向锁线程锁记录空间中一定存在该lock的LockRecord记录。偏向锁撤销,就需要将偏向锁升级为“轻量级锁”。

轻量级锁:

  • 轻量级锁仍然无法提供线程互斥。是属于偏向锁和重量级锁中间的一个缓冲层。存在的意义是延迟重量级锁的到来,让重量级锁只有在真正需要线程互斥时再申请。

场景:
A和B两个线程,运行过程中获取了指定Lock,但是A拿锁时B不拿,B拿锁时A不拿,两个线程完美错开。这种情况依旧是不需要重量级锁。

轻量级锁加锁流程:第一步仍然是向线程栈的锁空间push一条LockRecord。Lock在被抢占之前,markword处于无锁状态,获取锁线程使用CAS替换Lock的markword,成功说明拿到了轻量级锁,失败说明被其他线程正在抢占,或者是当前线程是锁重入

CAS成功,说明本线程是第一次拿到该lock的轻量级锁,需要将插入的LockRecord的displacedMarkWord设置为无锁状态的markword值。

重量级锁

  • 在jvm中由objectMonitor实现。默认情况下(开启偏向锁,轻量级支持),只有真正需要互斥时,才会将lock对象膨胀为重量级锁。

场景: 线程A持有锁,且一直未释放。线程B也去申请这把锁,因为A未释放,导致B需要等待A释放。偏向锁和轻量级锁都无法实现让B等待的功能,此时重量级锁就使用。

重量级锁膨胀流程:
假设当前锁处于"偏向锁状态"且偏向A,并且A处于持有状态。B线程去获取锁失败,首先提交偏向锁撤销的任务交给VM线程完成,VM线程会在安全点将偏向A的锁升级为“轻量级锁”且持锁线程为A。VM线程执行完“撤销任务”后,B线程继续运行。尝试抢“轻量级锁”,假设A还没有释放,依然会失败,至此,B推测出线程确实占用。B线程接下来会做锁膨胀逻辑,无限自旋,只有膨胀完成后才会退出,退出时返回锁对象的ObjectMonitor实列。

自旋的逻辑:

  • CAS修改锁对象markword为"膨胀中即0值"
  • 如果成功,当前线程将负责膨胀逻辑,否则当前线程自旋等待其他线程完成膨胀。再返回其他线程为lock申请的objectMonitor。
  • 负责膨胀的线程,到ObjectMonitorPool中申请一个空闲的ObjectMonitor实列,初始化好ObjectMonitor实列的字段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

virtuousOne

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值