ToB企服应用市场:ToB评测及商务社交产业平台

标题: JavaEE之CAS [打印本页]

作者: 王國慶    时间: 9 小时前
标题: JavaEE之CAS
上文我们熟悉了许很多多的锁,此篇我们的CAS就是从上文的锁计谋开展的新概念,我们来一探毕竟吧
  1. 什么是CAS?

CAS: 全称Compare and swap,字⾯意思:“比力并交换”,⼀个CAS涉及到以下操作:
我们假设内存中的原数据V,旧的预期值A,必要修改的新值B。
CAS伪代码
下面写的代码不是原子的,真实的CAS是⼀个原子的硬件指令完成的.这个伪代码只是辅助明白CAS的工作流程.
  1. while{
  2. boolean CAS(address, expectValue, swapValue) {
  3. if (&address == expectedValue) {
  4. &address = swapValue;
  5. return true;
  6. }
  7. return false;
  8. }
  9. }
复制代码
说明:
执行流程:
我们前面学习过的线程安全题目是由于CPU的随机调度导致的,我们的处理方法是给有关修改共享变量的代码块加了synchronize关键字,保证了原子性,解决了线程安全题目,
那么题目来了,
为什么我们看到的CAS伪代码并没有保证原子性,那么他是如何保证原子性的呢??
我们从一段例子来讲解:




总结:
2. CAS实现自旋锁

基于CAS实现更灵活的锁,获取到更多的控制权.
自旋锁伪代码:
  1. public class SpinLock {
  2. private Thread owner = null;
  3. public void lock(){
  4. // 通过CAS 看当前锁是否被某个线程持有.  
  5. // 如果这个锁已经被别的线程持有, 那么就⾃旋等待.  
  6. // 如果这个锁没有被别的线程持有, 那么就把owner 设为当前尝试加锁的线程.  
  7. }
  8. }
  9. while(!CAS(this.owner, null, Thread.currentThread())){
  10. }
  11. public void unlock (){
  12. this.owner = null;
  13. }
复制代码
3. JDK中基于CAS实现的原子类

说明:
  1. //自增 ++i
  2. atomicInteger.getAndIncrement();
复制代码
  1. public final int getAndIncrement() {
  2.         return U.getAndAddInt(this, VALUE, 1);
  3. }
复制代码
  1. private static final Unsafe U = Unsafe.getUnsafe();
  2. //获取内存中字段在对象中的偏移地址
  3.     private static final long VALUE
  4.         = U.objectFieldOffset(AtomicInteger.class, "value");
复制代码
  1. //1. o表示原子类对象
  2. //2. offset表示内存中字段在对象中的偏移地址
  3. //3. delta表示传入的值
  4. public final int getAndAddInt(Object o, long offset, int delta) {
  5.         int v;
  6.         do {
  7.         //v是CAS操作之前获取的预期值
  8.             v = getIntVolatile(o, offset);
  9.         } while (!weakCompareAndSetInt(o, offset, v, v + delta));
  10.         //while不停的自旋
  11.         return v;
  12.     }
复制代码
  1.    public final boolean weakCompareAndSetInt(Object o, long offset,//内存地址
  2.                                               int expected,//预期值
  3.                                               int x) {//要设置的值
  4.         return compareAndSetInt(o, offset, expected, x);//最后调用compareAndSetInt()方法
  5.     }
复制代码
  1. //native表示本地方法,即对应了一条CAS指令cmpxchg
  2. public final native boolean compareAndSetInt(Object o, long offset,
  3.                                                  int expected,
  4.                                                  int x);
复制代码
4. CAS的ABA题目

首先我们要知道什么是ABA题目???
ABA的题目:
假设存在两个线程t1和t2.有一个共享变量value,初始值为A.
接下来,线程t1想使用CAS把value值改成Z,那么就必要
先读取value的值,记录到oldValue变量中.
使用CAS判断当前value的值是否为A,如果为A,就修改成Z.
但是,在t1执行这两个操作之间,t2线程可能把value的值从A改成了B,又从B改成了A
   举个例子: 今天是疯狂星期四,我和我的女友共用一个小荷包用饭,我和她说如果小荷包内里有100元 (A状态) 晚上6点就点KFC吃,我怕她忘记了,我就先自己点了,此时,小荷包余额为50元 (B状态) 然后我的朋友疯狂星期四V我50,小荷包的钱又变回了100 (A状态),她再点的时候,发现小荷包还是100元,她就再点了一份,就导致多点了一份KFC
  那么我们该如何解决ABA题目呢??
在点KFC的例子当中,我们可以让他查察一下斲丧记录(版本号),此时的100元是否是开始的100元,
由此可得,我们可以可以给value数据加上一个版本号,当线程2要修改时,不仅要value要符合预期值,同时版本号也要符合
即:
CAS操作在读取旧值的同时,也要读取版本号.
真正修改的时候,

版本号可以用自增的数值,也可以用随机的UUID,也可以使用时间戳
  1. System.out.println(UUID.randomUUID());
复制代码
天生随机数:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4