怎么制作网站app,国外建站公司,湖南关键词排名推广,网站建站收费什么是CAS CAS即Compare And Swap的缩写#xff0c;翻译成中文就是比较并交换#xff0c;其作用是让CPU比较内存中某个值是否和预期的值相同#xff0c;如果相同则将这个值更新为新值#xff0c;不相同则不做更新#xff0c;也就是CAS是原子性的操作(读和写两者同时具有原…什么是CAS CAS即Compare And Swap的缩写翻译成中文就是比较并交换其作用是让CPU比较内存中某个值是否和预期的值相同如果相同则将这个值更新为新值不相同则不做更新也就是CAS是原子性的操作(读和写两者同时具有原子性)其实现方式是通过借助C/C调用CPU指令完成的所以效率很高。CAS的原理很简单这里使用一段Java代码来描述 public boolean compareAndSwap(int value, int expect, int update) {
// 如果内存中的值value和期望值expect一样 则将值更新为新值updateif (value expect) {value update;return true;} else {return false;}
}
复制代码大致过程是将内存中的值、我们的期望值、新值交给CPU进行运算如果内存中的值和我们的期望值相同则将值更新为新值否则不做任何操作。这个过程是在CPU中完成的这里不好描述CPU的工作过程就拿Java代码来描述了。 Unsafe源码分析 Java是在Unsafe(sun.misc.Unsafe)类实现CAS的操作而我们知道Java是无法直接访问操作系统底层的API的(原因是Java的跨平台性限制了Java不能和操作系统耦合)所以Java并没有在Unsafe类直接实现CAS的操作而是通过**JDI(Java Native Interface)**本地调用C/C语言来实现CAS操作的。Unsafe有很多个CAS操作的相关方法这里举例几个 public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
复制代码我们拿public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);进行分析这个方法是比较内存中的一个值(整型)和我们的期望值(var4)是否一样如果一样则将内存中的这个值更新为var5参数中的var1是值所在的对象var2是值在对象(var1)中的内存偏移量参数var1和参数var2是为了定位出值所在内存的地址。Unsafe.java在这里发挥的作用有 将对象引用、值在对象中的偏移量、期望的值和欲更新的新值传递给Unsafe.cpp如果值更新成功则返回true给开发者没有更新则返回falseUnsafe.cpp在这里发挥的作用有 接受从Unsafe传递过来的对象引用、偏移量、期望的值和欲更新的新值根据对象引用和偏移量计算出值的地址然后将值的地址、期望的值、欲更新的新值传递给CPU如果值更新成功则返回true给Unsafe.java没有更新则返回falseCPU在这里发挥的作用 接受从Unsafe.cpp传递过来的地址、期望的值和欲更新的新值执行指令cmpxchg比较地址中的值是否和期望的值一样一样则将值更新为新的值不一样则不做任何操作将操作结果返回给Unsafe.cppCAS的缺点 CAS虽然高效的实现了原子性操作但是也存在一些缺点主要表现在以下三个方面。 ABA问题 在多线程场景下CAS会出现ABA问题关于ABA问题这里简单科普下例如有2个线程同时对同一个值(初始值为A)进行CAS操作这三个线程如下 线程1期望值为A欲更新的值为B线程2期望值为A欲更新的值为B线程1抢先获得CPU时间片而线程2因为其他原因阻塞了线程1取值与期望的A值比较发现相等然后将值更新为B然后这个时候出现了线程3期望值为B欲更新的值为A线程3取值与期望的值B比较发现相等则将值更新为A此时线程2从阻塞中恢复并且获得了CPU时间片这时候线程2取值与期望的值A比较发现相等则将值更新为B虽然线程2也完成了操作但是线程2并不知道值已经经过了A-B-A的变化过程。 ABA问题带来的危害 小明在提款机提取了50元因为提款机问题有两个线程同时把余额从100变为50 线程1提款机获取当前值100期望更新为50 线程2提款机获取当前值100期望更新为50 线程1成功执行线程2某种原因block了这时某人给小明汇款50 线程3默认获取当前值50期望更新为100 这时候线程3成功执行余额变为100 线程2从Block中恢复获取到的也是100compare之后继续更新余额为50 此时可以看到实际余额应该为100100-5050但是实际上变为了50100-5050-50这就是ABA问题带来的成功提交。 解决方法 在变量前面加上版本号每次变量更新的时候变量的版本号都1即A-B-A就变成了1A-2B-3A。 循环时间长开销大 如果CAS操作失败就需要循环进行CAS操作(循环同时将期望值更新为最新的)如果长时间都不成功的话那么会造成CPU极大的开销。 这种循环也称为自旋 解决方法 限制自旋次数防止进入死循环。 只能保证一个共享变量的原子操作 CAS的原子操作只能针对一个共享变量。 解决方法 如果需要对多个共享变量进行操作可以使用加锁方式(悲观锁)保证原子性或者可以把多个共享变量合并成一个共享变量进行CAS操作。 CAS的应用 我们知道CAS操作并不会锁住共享变量也就是一种非阻塞的同步机制CAS就是乐观锁的实现。 乐观锁 乐观锁总是假设最好的情况每次去操作数据都认为不会被别的线程修改数据所以在每次操作数据的时候都不会给数据加锁即在线程对数据进行操作的时候别的线程不会阻塞仍然可以对数据进行操作只有在需要更新数据的时候才会去判断数据是否被别的线程修改过如果数据被修改过则会拒绝操作并且返回错误信息给用户。悲观锁 悲观锁总是假设最坏的情况每次去操作数据时候都认为会被的线程修改数据所以在每次操作数据的时候都会给数据加锁让别的线程无法操作这个数据别的线程会一直阻塞直到获取到这个数据的锁。这样的话就会影响效率比如当有个线程发生一个很耗时的操作的时候别的线程只是想获取这个数据的值而已都要等待很久。Java利用CAS的乐观锁、原子性的特性高效解决了多线程的安全性问题例如JDK1.8中的集合类ConcurrentHashMap、关键字volatile、ReentrantLock等。 参考 JAVA CAS原理深度分析Java CAS 原理分析什么是ABA问题 原文地址ddnd.cn/2019/03/13/… 转载于:https://juejin.im/post/5c87afa06fb9a049f1550b04