【面试造火箭1】之Synchronized Volatile底层实现

   随着新的一年钟声逐渐靠近,又到了春天招聘的季节。程序猿们内心也蠢蠢欲动,拨动自己的小算盘,开始筹划着新一轮的换工作热潮。

 

这一天,程序员们终于想起「面试」支配的恐惧。图片

 

图片

 

众所周知,现在的程序员招聘难度是越来越高了,正所谓面试造火箭,入场扭螺丝~图片其实吧,我觉得面试造火箭,程序猿技术内卷并不是太糟糕的事情,因为这可以促进行业技术发展,随着时代的迭代更新,程序猿们并不单单只需要熟练运用一门语言,他们更多的是需要结合各种高性能高可用的框架,写出高可扩展性的程序。

 

技术内卷不可怕,可怕的是比你优秀的人都在努力,你有什么资格不去努力~图片图片

 

今天我准备出这个系列的知识总结,作为我今后对知识的积累,也希望自己能在持续长久的更新下去。其实,这个系列已经烂大街了~随便一搜3y、敖丙,哪个大佬写的不比我强一万倍?但我还是需要自己总结一份属于自己的知识管理,各位有兴趣的可以看看~

 

基于最近读了《深入理解Java虚拟机》、《Java并发编程实战》、马士兵、3y等书籍文稿视频,我也整理出一份synchronized和volatile的相关知识。

 

 


 

 

Synchronized关键字

synchronized

synchronized是重量级锁,每次使用synchronized都是JVM层面向内核OS操作系统进行申请锁的过程。

synchronized关键字通过Javac编译后,会在同步块的前后分别形成monitorenter、monitorexit这两个字节码指令,并且这两个指令都需要关联一个reference参数来指明要锁定和解锁的对象。

 

 

Markword对象头

相较于重量级锁,Java在1.6以后引入的偏向锁、轻量级锁在JVM层级做了锁逻辑,将不依赖以底层OS操作系统,不存在内核上下文切换消耗。

 

  • Java到底是如何区分识别和使用这些锁的呢?

当标志位分别为 :001无锁,101偏向锁,00轻量级/自旋锁,10重量级锁。其他的参数诸如hashcode,线程指针和LockRecord,将会在锁升级部分用到。

  • 对象内部布局是?

内部布局包含4个部分,分别是:markword对象头(8字节)class pointer类型指针(4字节),instance data实例对象,padding对齐(使对象能被8字节整除)

 

图片

(对象头8字节64位)

 

 

CAS

CAS 英文名为compare and swap,意思是比较并交换,即在并发的场景下不用锁的形式进行修改,Java的实现是通过JVM中的C++的汇编指令实现:lock compxchg。

  • CAS实现

三个变量,当前值E,修改值V,最新值N。只有在E==N的时候才能把V赋值为E。

  • ABA问题

通常做法是加一个版本号,区别当前版本和之前版本不一致。

 

 

 

锁升级

  • 在介绍锁升级之前,这里简单介绍一下各种锁的作用

    偏向锁:Java在无竞争的情况下,会优先使用偏向锁,并在Markword中存储当前线程id

    轻量级锁:Java在轻度竞争的情况下,会使用轻量级锁,线程通过CAS修改LR指针,轻量      级锁会不断自旋地CAS并获取锁。

    重量级锁:Java在重度竞争的情况下,会使用重量级锁,直接调用OS操作系统实现

  • 锁升级

    1. 当Javac编译一行带synchronized关键字的对象时,在无竞争的情况下首先会优先使用偏向锁,因为经过Java内核开发的调查,大部分并发都不会发生竞争,如果每次都升级成重度锁,那实在是太消耗CPU了

    2. 当对象存在其他线程使用,发生了CAS修改,并修改线程指针为LR指针,偏向锁升级为自旋锁(轻量级锁),当轻量级锁遇到了竞争会不断地CAS自旋的修改LR指针,直到获取到锁为止。

    3. 当CAS自旋超过了10次,锁升级为重度锁,由OS操作系统通过Object Monitor管理,并维护着waitSet等待队列和entryList竞争队列。

     

 

Volatile关键字

与锁相比,volatile变量是一种更轻量级的同步机制,,因为在使用这些变量时不会发生上下文切换或者线程调度操作。

 

Volatile关键字有两个关键做用:

线程间可见禁止指令重排

 

图片

 

在物理计算机的并发问题中,处理器和主内存的运算速度是有很大区别的,如果每次要用数据都从主内存中获取,那么效率总是会比较慢的。所以计算机引入了高速缓存,先将数据缓存起来,运算结束后再还给主内存。

所以在这种环境下没有加volatile关键字的变量线程间是不会同步的,而加了volatile以后Java会要求JVM让内存与缓存的数据保持一致,通过内存屏障来实现。

  • 在JVM层面:内存屏障通过实现(Load读取,Save保存)LL,SS,LS,SL间的指令屏障不可交换(缓存一致性协议)

  • 在CPU层面:Java要求CPU与主内存之间的变量进行一个锁总线的操作,使得其他CPU的高速缓存无法获取到已改变的变量

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值