并发机制的底层实现原理||

Volatile实现原理

Lock前缀的指令会引起处理器缓存到内存
一个处理器的缓存回写到内存会导致其他处理器的缓存无效,故其他地方的缓存会重新读内存,从而实现了不同处理器上的缓存是同步的

java 的每一个对象都可以作为锁,
  • 对于普通同步对象,锁是当前实例对象
  • 对于静态同步方法,锁是当前类的class对象
  • 对于同步方法块,锁是synchonized括号里配置的对象
    jvm基于进入和退出monitor对象来实现方法同步和代码块同步,两者的实现细节不一样, monitorenter指令时在编译后插入到同步代码块开始的位置,而moniterexit是插入到方法结束处和异常处,jvm要保证每一个moniterenter必须有对应的moniterexit与之对应。
java对象头存储结构

锁状态,hashcode,对象的分代年龄,是否是偏向锁,锁标志位

锁的状态

锁一共有四种状态,级别从低到高依次为无锁状态,偏向锁,轻量级锁,重量级锁。锁可以升级但是不能降级,这种锁升级确不能降级的策略,目的是为了提高获得锁和释放锁的效率。

偏向锁

偏向锁:大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获得锁时,会在对象头和栈帧中的锁记录中存储锁偏向的的线程ID,以后线程在进入和退出同步块是不需要进行CAS操作来加锁和解锁,只需要简单的测试一下对象头是否存储当前线程的偏向锁。
在这里插入图片描述

轻量级锁

轻量级锁加锁
线程在执行同步块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间中,并且将对象头中的make word复制在锁记录中,然后线程尝试使用cas将对象头中的make word指向锁记录的指针,如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获得锁
轻量级锁解锁
轻量级锁解锁是,会使用原子的cas操作将mark work替换会对象头,如果成功,则表示没有竞争发生,如果失败,表示锁会发生竞争,锁会膨胀成重量级锁,
在这里插入图片描述因为自旋会消耗cpu,为了避免无用的自旋,一旦锁升级成重量级锁,就不会恢复到轻量级锁,当锁处于这个状态是,其他线程无法获得锁都会被阻塞,当持有锁的线程释放锁之后会唤醒这些线程,这些线程会进行新一轮的夺锁之争
锁的优缺点比较
在这里插入图片描述

处理器提供总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性。
  • 使用总线保证原子性:所谓的总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上上输出此信号时,其他的处理器的请求将阻塞住,那么该处理器可以独占内存共享
  • 使用缓存锁定来保证原子性:内存区域如果被缓存在处理器的缓存行中,并且在LOCK期间被锁定,那么他执行所操作回写到内存中时,处理器不在总线上声明LOCK#信号,而是修改内部的内存地址,并且允许他的缓存一致性来保证操作的原子性,因为缓存一致性机制会阻止同时修改两个以上的处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时,会使得缓存行无效。
CAS原子操作存在的几个问题
  • ABA问题:A->B->A会使得CAS误判断,通常需要加入版本号的方法来解决
  • 循环时间长开销大:如果自旋CAS长时间不成功,会给CPU带来非常大的开销,如果JVM能够支持处理器提供的pause指令,那么效率会有一定的提升。
  • 只能保证一个共享变量的原子操作:但是有一个取巧的办法,把多个共享变量合并成一个共享变量来操作,
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值