synchronized底层原理(重量级锁,轻量级锁,偏向锁)

目录

说⼀说⾃⼰对于 synchronized 关键字的了解

使用方法:

monitor原理

各种锁

重量级锁

自旋锁(循环上锁)

轻量级锁的实现

锁的释放

偏向锁

wait notify notifyAll


synchronized 是什么

用在多线程领域,使线程同步,synchronized(class) 加一个对象就可以上锁

这样就可以避免一些线程安全问题(访问共享资源,读写共享资源,临界区)

使用方法:

用代码块或者加在方法上。

在Java早期版本中, synchronized属于重量级锁,效率低下,因为监视器锁( monitor)是依赖于底层的操作系统的 Mutex Lock来实现的,Java的线程是映射到操作系统的原生线程之上的。

如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized效率低的原因。

庆幸的是在Java6之后Java官方对从JVM层面对 synchronized较大优化,所以现在的 synchronized锁效率也优化得很不错了。

java1.6 对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

monitor原理

Monitor 被翻译为 监视器 管程
每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针。

各种锁

重量级锁

和monitor相关联属于重量级锁,因为有和操作系统的交互,会占用较多资源,所以有了轻量级锁和偏向锁。三者可以在对象的markword字段中后三位二进制数得到,01是正常状态和偏向锁状态,00是轻量级锁,10是重量级锁。

自旋锁(循环上锁)

重量级锁竞争的时候可以使用自旋来进行优化。

重量级锁会有阻塞,阻塞再唤醒就会有线程的上下文切换,效率较低。

于是在一个线程上锁时发现对象已经被占用,这时不会立马进入阻塞队列,而是会连续几次进行上锁的尝试。

这样会减少一些线程的阻塞,提升效率。自旋锁在单核cpu下没有意义,反而降低程序效率。

轻量级锁的实现

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

如果没有锁膨胀,轻量级锁没有和操作系统的交互。

创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word 。

在进行锁定时,通过CAS(compare and swap)和该对象头中的mark word进行比较交换,如果成功了,那么此时 锁记录中就会存放对象头的mark word( hashcode epoch age等数据),对象的mark word就会被替换为一个64位的二进制数(末尾是00 表明是轻量级锁),表示该对象被某个线程锁了。

 

如果CAS不成功,就会进入锁重入或者锁膨胀(升级为重量级锁)

上锁过程,如果该线程的子方法里面还要加锁,那么就会发生锁重入,即再在前面的栈帧上方建立新的栈帧,里面同样维护着一个lock record内存,引用的对象依然时同一个,不过存放mark word 的地方存放null,因为不能进行CAS了。

锁膨胀就是当另一个线程想要CAS一个对象时,发现该对象已经被上锁,那么这时就会进入锁膨胀:

为该对象申请一个 monitor对象,然后第一个线程的栈帧中锁记录中的的object的地址就不是刚才的轻量级锁的标识了,就会指向monitor的地址,表明自己是一个重量级锁锁定的对象,同时后一个线程进入 entrylist,阻塞。

 

锁的释放

线程释放锁的时候如果CAS成功,那么说明没有发生锁膨胀,正常CAS设置锁对象的mark word即可。

线程释放锁的时候,CAS失败,就会进入重量级锁的解锁流程,即找到monitor对象,把owner设置为null,唤醒entrylist中的blocked线程。

这样的话就比重量级锁的开销更小,效率更高。

偏向锁

进一步优化轻量级锁(CAS导致效率变低),使用线程id来替换markword

而不是采用锁记录CAS markword。

每次只需要检查id是不是一个线程的即可。

wait notify notifyAll

参考:

黑马 JUC

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
无状态偏向锁轻量级重量级都是Java中的机制,它们的实现方式和性能表现不同。 无状态:也称为自旋,当线程尝试获取时,如果已经被其他线程占用,该线程会一直自旋等待的释放,直到获取到为止。这种适用于的持有时间非常短的情况,因为长时间的自旋会浪费CPU资源。 偏向锁偏向锁是一种针对加操作的优化手段,它的目标是减少无竞争情况下的操作的性能消耗。当一个线程访问一个偏向锁时,它会将对象头中的标识位设置为偏向,并将线程ID记录在对象头中。之后,该线程再次请求时,无需再次竞争,直接获取即可。这种适用于只有一个线程访问对象的情况。 轻量级轻量级是一种针对多线程竞争情况下的优化手段,它的目标是减少线程阻塞的时间,提高程序的并发性能。当一个线程访问一个轻量级时,它会将对象头中的标识位设置为轻量级,并将对象的指针保存在线程的栈帧中。之后,其他线程再次请求时,会通过自旋的方式尝试获取,而不是阻塞等待。如果自旋失败,就会升级为重量级。这种适用于的竞争不是很激烈的情况。 重量级重量级是一种针对多线程竞争情况下的优化手段,它的目标是保证线程的正确性和程序的稳定性。当一个线程访问一个重量级时,它会进入阻塞状态,直到被释放。这种适用于的竞争非常激烈的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

trigger333

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

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

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

打赏作者

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

抵扣说明:

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

余额充值