写点什么

「Java」手把手理解 CAS 实现原理,学习 linux 编程

用户头像
极客good
关注
发布于: 刚刚

参数 var1:表示要操作的对象


参数 var2:表示要操作对象中属性地址的偏移量


参数 var4:表示需要修改数据的期望的值


参数 var5:表示需要修改为的新值


  • 此处描述一下?偏移量?的概念?



这里的偏移量就像我们【new】一个对象,对象的地址就是【0x001】,那么 value 的地址就是【0x002 = 0x001 + 1】,


【+1】就是偏移量。


  • CAS 的实现原理是什么?


CAS 通过调用 JNI 的代码实现(JNI:Java Native Interface),允许 java 调用其他语言,


而【compareAndSwapXXX】系列的方法就是借助“C 语言”来调用 cpu 底层指令实现的。


以常用的【Intel x86】平台来说,最终映射到 cpu 的指令为【cmpxchg】(compareAndChange),


这是一个原子指令,cpu 执行此命令时,实现比较替换操作。


  • 那么问题来了,现在计算机动不动就上百核,【cmpxchg】怎么保证多核下的线程安全?


系统底层进行 CAS 操作时,会判断当前系统是否为多核系统,如果是,就给【总线】加锁,


只有一个线程对总线加锁成功, 加锁成功之后会执行 CAS 操作,也就是说 CAS 的原子性是平台级别的。


  • 那么问题又来了,CAS 这么流批,就不会有什么问题么?


1》高并发下,其他线程会一直处于自旋阻塞状态


2》ABA 问题(重要)


  • 什么是 ABA 问题呢?


CAS 需要在操作值的时候,检查下值有没有发生变化,如果没有发生变化则更新,


但是可能会有这样一个情况,如果一个值原来是 A,在 CAS 方法执行之前,被其他线程修改为了 B,然后又修改回成 A,


此时 CAS 方法执行之前,检查的时候发现它的值并没有发生变化,但实际却变化了,这就是【CAS 的 ABA】问题。



  • 话不多说,我们这里用代码来模拟一下 ABA 问题:


public class CasABADemo1 {


private static Atomi


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


cInteger count = new AtomicInteger(0);


public static void main(String[] args) {


System.out.println("mainThread 当前 count 值为: " + count.get());


Thread mainThread = new Thread(() -> {


try {


int expectCount = count.get();


int updateCount = expectCount + 1;


System.out.println("mainThread 期望值:" + expectCount + ", 修改值:" + updateCount);


Thread.sleep(2000);//休眠 2000s ,释放 cpu


boolean result = count.compareAndSet(expectCount, updateCount);


System.out.println("mainThread 修改 count : " + result);


} catch (InterruptedException e) {


e.printStackTrace();


}


});


Thread otherThread = new Thread(() -> {


try {


Thread.sleep(20);//确保主线程先获取到 cpu 资源


} catch (InterruptedException e) {


e.printStackTrace();


}


count.incrementAndGet();


System.out.println("其他线程先修改 count 为:" + count.get());


count.decrementAndGet();


System.out.println("其他线程又修改 count 为:" + count.get());


});


mainThread.start();


otherThread.start();


}


}


结果:


mainThread 当前 count 值为: 0


mainThread 期望值:0, 修改值:1


其他线程先修改 count 为:1


其他线程又修改 count 为:0


mainThread 修改 count : true


最后结果可以看出【mainThread】修改成功,但是【mainThread】获取到的【expectCount】虽然也是 1,但已经不是曾经的【expectCount】。


  • 如何解决 ABA 问题呢?


解决 ABA 最简单的方案就是给值加一个版本号,每次值变化,都会修改他的版本号,


CAS 操作时都去对比次版本号。


  • java 中提供了一种版本号控制的方法,可以解决 ABA 问题:


public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
「Java」手把手理解CAS实现原理,学习linux编程