欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

18.JAVA的CAS操作

程序员文章站 2022-05-04 15:13:46
...

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类

18.JAVA的CAS操作

18.JAVA的CAS操作

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

18.3ABA问题

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

18.JAVA的CAS操作

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

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问题(原子引用)

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

18.JAVA的CAS操作

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();


    }
}
相关标签: java多线程