18.JAVA的CAS操作

18.CAS

18.1CAS简介

CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
缺点:
1、 循环会耗时(因为底层是自旋锁)
2、一次性只能保证一个共享变量的原子性
3、会存在ABA问题

package com.CAS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Created by yj on 2020/9/5 14:49
 */
public class CASDemo {

    //AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题

    // 正常在业务操作,这里面比较的都是一个个对象
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);

    // CAS  compareAndSet : 比较并交换!
    public static void main(String[] args) {

        AtomicInteger atomicInteger = new AtomicInteger(2020);
        //期望、更新
        //public final boolean compareAndSet(int expect, int update)
        //如果期望值达到了,就更新,如果没达到就不更新.CAS是CPU的并发原语
        atomicInteger.compareAndSet(2020,2021);//这儿由于上面给这个atomicInteger传入的是2020,所以是期望值,所以更新为2021
        atomicInteger.compareAndSet(2020,2021);//这儿上面更新为了2021,此时不为期望值,更新失败
        System.out.println(atomicInteger.get());
        atomicInteger.getAndIncrement();//i++

    }
}

18.2unsafe类

image-20200905212730963

image-20200905213810505

上面这个其实就是自旋锁。

18.3ABA问题

就是右边线程比较快,达到A后进行cas操作,cas(1,3)和cas(3,1)后A又变回了1,但是左边线程不知道,直接拿到了这个A=1,其实他是经过右边线程进行了两次cas操作后的结果,但是左边线程并不知道,认为A的值就没变过。

image-20200905222020343

这个例子就是描述的前面两步操作将值最后变为原样,最后一步又取这个值,但是它不知道这一步是经过变换的.

package com.CAS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Created by yj on 2020/9/5 14:49
 */
public class CASDemo {

    //AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题

    // 正常在业务操作,这里面比较的都是一个个对象
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);

    // CAS  compareAndSet : 比较并交换!
    public static void main(String[] args) {

        AtomicInteger atomicInteger = new AtomicInteger(2020);
        
        //=========这儿两次操作把值又调回了2020===========
        System.out.println(atomicInteger.compareAndSet(2020,2021));//这儿由于上面给这个atomicInteger传入的是2020,所以是期望值,所以更新为2021
        System.out.println(atomicInteger.get());
        
        System.out.println(atomicInteger.compareAndSet(2021,2020));//这儿上面更新为了2021,此时不为期望值,更新失败
        System.out.println(atomicInteger.get());
        
        //==========这儿并不知道拿到的值是经过变换的=========
        System.out.println(atomicInteger.compareAndSet(2020,2021));//这儿上面更新为了2021,此时不为期望值,更新失败
        System.out.println(atomicInteger.get());

    }
}

18.4解决ABA问题(原子引用)

带时间戳的原子引用,为什么它可以解决这个问题,其实就类似于给了一个版本号,每次操作给予时间戳一定的变换.

image-20200905235603762

package com.CAS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Created by yj on 2020/9/5 14:49
 */
public class CASDemo {

    //AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
   /**
    *Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实
    *例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;*/
   // 正常在业务操作,这里面比较的都是一个个对象,所以不会出现问题,这儿因为测试使用了包装类,其范围有限定,所以出问题了
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);

    // CAS  compareAndSet : 比较并交换!
    public static void main(String[] args) {

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp(); // 获得版本号
            System.out.println("a1=>"+stamp);

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Lock lock = new ReentrantLock(true);
            //参数(期望的值,新的值,时间戳,时间戳+1)
            atomicStampedReference.compareAndSet(1, 2,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);

            System.out.println("a2=>"+atomicStampedReference.getStamp());


            System.out.println(atomicStampedReference.compareAndSet(2, 1,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));

            System.out.println("a3=>"+atomicStampedReference.getStamp());

        },"a").start();


        // 乐观锁的原理相同!
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp(); // 获得版本号
            System.out.println("b1=>"+stamp);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(atomicStampedReference.compareAndSet(1, 6,
                    stamp, stamp + 1));

            System.out.println("b2=>"+atomicStampedReference.getStamp());

        },"b").start();


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值