关于 Synchronized 的一个点

而关于 Synchronized 我去年还专门翻阅 JVM HotSpot 1.8 的源码来研究了一波,那时候我就发现有一个点,一个几乎网上所有文章包括《Java并发编程的艺术》也是这样说的一个点。

锁升级想必网上有太多文章说过了,这里提到当轻量级锁 CAS 失败,则当前线程会尝试使用自旋来获取锁。

其实起初我也是这样认为的,毕竟都是这样说的,而且也很有道理。

因为重量级锁会阻塞线程,所以如果加锁的代码执行的非常快,那么稍微自旋一会儿其他线程就不需要锁了,就可以直接 CAS 成功了,因此不用阻塞了线程然后再唤醒。

但是我看了源码之后发现并不是这样的,这段代码在 synchronizer.cpp 中。

所以 CAS 失败了之后,并没有什么自旋操作,如果 CAS 成功就直接 return 了,如果失败会执行下面的锁膨胀方法。

我去锁膨胀的代码ObjectSynchronizer::inflate翻了翻,也没看到自旋操作。

所以从源码来看轻量级锁 CAS 失败并不会自旋而是直接膨胀成重量级锁。

不过为了优化性能,自旋操作在 Synchronized 中确实却有。

那是在已经升级成重量级锁之后,线程如果没有争抢到锁,会进行一段自旋等待锁的释放。

咱们还是看源码说话,单单注释其实就已经说得很清楚了:

毕竟阻塞线程入队再唤醒开销还是有点大的。

我们再来看看 TrySpin 的操作,这里面有自适应自旋,其实从实际函数名就 TrySpin_VaryDuration 就可以反映出自旋是变化的。

至此,有关 Synchronized 自旋问题就完结了,重量级锁竞争失败会有自旋操作,轻量级锁没有这个动作(至少 1.8 源码是这样的),如果有人反驳你,请把这篇文章甩给他哈哈。

不过都说到这儿了,索性我就继续讲讲 Synchronized 吧,毕竟这玩意出镜率还是挺高的。

这篇文章关于 Synchronized 的深度到哪个程度呢?

之后如有面试官问你看过啥源码?

看完这篇文章,你可以回答:我看过 JVM 的源码。

当然源码有点多的,我把 Synchronized 相关的所有操作都过了一遍,还是有点难度的。

不过之前看过我的源码分析的读者就会知道,我都会画个流程图来整理的,所以即使代码看不懂,流程还是可以搞清楚的!

好,发车!

从重量级锁开始说起

Synchronized 在1.6 之前只是重量级锁。

因为会有线程的阻塞和唤醒,这个操作是借助操作系统的系统调用来实现的,常见的 Linux 下就是利用 pthread 的 mutex 来实现的。

我截图了调用线程阻塞的源码,可以看到确实是利用了 mutex。

而涉及到系统调用就会有上下文的切换,即用户态和内核态的切换,我们知道这种切换的开销还是挺大的。

所以称为重量级锁,也因为这样才会有上面提到的自适应自旋操作,因为不希望走到这一步呀!

我们来看看重量级锁的实现原理

Synchronized 关键字可以修饰代码块,实例方法和静态方法,本质上都是作用于对象上。

代码块作用于括号里面的对象,实例方法是当前的实例对象即 this ,而静态方法就是当前的类。

这里有个概念叫临界区。

我们知道,之所以会有竞争是因为有共享资源的存在,多个线程都想要得到那个共享资源,所以就划分了一个区域,操作共享资源资源的代码就在区域内。

可以理解为想要进入到这个区域就必须持有锁,不然就无法进入,这个区域叫临界区。

当用 Synchronized 修饰代码块时

此时编译得到的字节码会有 monitorenter 和 monitorexit 指令,我习惯按照临界区来

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值