java cas原理解析

本文内容是假定读者已经有一些多线程的工作经验以及一些计算机系统原理知识的前提下进行介绍的,所以可能并不是说的很通俗易懂,也仅是个人作为一个知识的总结分享。

相信大家在开发过程中对java的cas操作并不陌生,cas作为一种乐观锁的实现方式,我们在java.util.concurrent包下可以看到很多类都使用了cas的方式解决多线程并发问题:

可以看到这些api的底层都是调用了Unsafe的方法, 我们在跟进去就是native方法了:

基本上好多同学就被劝退了。也确实,只要JVM保证这个cas的语义。我们并不需要关心具体JVM如何实现,这也是很多系统间进行分层的原因,屏蔽底层的细节。我今天要讲的就是具体这个native方法又是如何实现的。

首先大家都知道cas操作底层是靠cpu来保证原子性的,cpu仅仅只在简单的操作上保证原子性,比如cpu读取内存值,写入内存值等。另外也提供了一些特殊的操作指令,并且保证他们的执行是原子性的,其中就有cmpxchg.翻译出来就是compare and swap:

 cas(expectvalue,newvalue)。将期望的值改为新值。这一点我们硬件层面有进行保证的原子指令。但是你仔细思考一下,如果仅仅是硬件指令就可以解决并发修改了吗?假设cas期间其他线程修改了值了。那不是当前的这次cas操作就失败了吗?还有就是ABA的问题,其他线程修改值后又改回原值了呢?大多数情况下ABA问题我们并不需要关心,但是特殊场景下,我们需要解决这种问题如何解决呢?JDK的源码给了思路那就是引入一个version版本号的机制来记录修改的次数,我们可以查看AtomicStampedReference源码:

封装了一个内部静态类Pair来包装引用和版本号,之所以这么设计的原因,其实主要是因为cas的操作目前只能针对一个值进行操作。而我们要解决版本号和引用是两个变量了。所以采用了这种合并的形式,来对pair对象进行cas操作:

 

有点偏题了。这是解决ABA的问题。现在继续说一下JVM如何实现cas的API:

 

 

 

 经过jvm的c++实现。最终底层其实主要是两个指令 lock  cmpxchg。那么这个lock指令是干嘛呢。其实我们语言层面说的乐观锁,其实在硬件层面在Lock指令这里属于悲观形式了。他会锁缓存或者系统总线并且无视中断直接指令完成。我们所有的内存数据与cpu交互都需要经过总线的。锁着总线就是让其他线程暂时无法访问只能有当前线程访问。锁缓存一个道理。只是粒度小一点。这就是cas底层硬件的实现方式。 

另外说明一个问题。就是我们进行i++测试的时候会使用AtomicInteger的类来进行计算。但是会存在一个循环cas的方法。当时我觉得如果其他线程改了值。那不是就会一直循环下去了吗?现在我们仔细看一下源码。其实并不是一直拿一个oldvalue替换成newValue的:

是每次都会读取当前的新值然后进行累加操作。这里需要当前值是volatile修饰才行! 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值