java_CAS

1、CAS:---> compareAndSet(期望值,修改值)

  比较并交换;

/**
 * CAS: ---> compareAndSet()
 *  比较并交换
 */
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5,2019)+" atomicInteger:"+atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5,1024)+" atomicInteger:"+atomicInteger.get());
    }
}

java_CAS

 

atomicInteger.getAndIncrement();为什么可以保证原子性?

java_CAS

因为底层使用的是Unsafe类:

java_CAS

java_CAS

CAS原理:

java_CAS

java_CAS

 

unsafe.getAndAddInt()

java_CAS

java_CAS

java_CAS

 

CAS原语:

java_CAS

 

CAS总结:

java_CAS

 

CAS的缺点:

  1、循环时间长,开销大;

  2、只能保证一个共享变量的原子操作;

  3、引发出来的ABA问题?

 

 

1、循环时间长,开销大;

java_CAS

2、只能保证一个共享变量的原子操作;

当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作;

但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候可以用锁来保证原子性;

 

3、引发的ABA问题?

java_CAS

 

 

JUC下的原子引用封装类:AtomicReference<V>

@Getter
@ToString
@AllArgsConstructor
class User{
    String name;
    int age;
}
public class AtomicReferenceDemo {
    public static void main(String[] args) {
        User z3 = new User("z3",22);
        User l4 = new User("l4",25);
        AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(z3);
        System.out.println(atomicReference.compareAndSet(z3,l4)+"\t"+atomicReference.get().toString());
        System.out.println(atomicReference.compareAndSet(z3,l4)+"\t"+atomicReference.get().toString());
    }
}

java_CAS

 

解决ABA问题?AtomicStampedReference<V>();

  原子引用+新增一种机制,例如版本号(类似于时间戳);

  AtomicStampedReference<V>();

    JUC下带有时间戳的原子引用类;

 

ABA问题产生的示例代码:

public class ABADemo {//ABA问题的解决 AtomicStampedReference<>();
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);

    public static void main(String[] args) {
        new Thread(() -> {//t1线程模拟ABA环境
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "t1").start();

        new Thread(() -> {
            //t2线程睡眠一秒,保证t1线程执行完ABA问题
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get());
        }, "t2").start();
    }
}

 java_CAS

 

ABA问题的解决示例代码:

public class ABADemo {//ABA问题的解决 AtomicStampedReference<>();
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);

    public static void main(String[] args) {
        System.out.println("====以下是ABA问题的产生====");
        new Thread(() -> {//t1线程模拟ABA环境
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "t1").start();

        new Thread(() -> {
            //t2线程睡眠一秒,保证t1线程执行完ABA问题
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get());
        }, "t2").start();

        //睡眠2秒,保证t1、t2线程完成
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }

        System.out.println("====以下是ABA问题的解决====");
        new Thread(() ->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t 第一次的版本号:"+stamp);
            //t3睡眠一秒,保证t3、t4拿到同一个版本号
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName()+"\t 第二次的版本号:"+atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName()+"\t 第三次的版本号:"+atomicStampedReference.getStamp());
        },"t3").start();

        new Thread(() ->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t 第一次的版本号:"+stamp);
            //t4睡眠3秒,保证t3、t4拿到同一个版本号,并且保证t3执行完ABA
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName()+"\t 是否修改成功:"+result
                    +"\t当前实际版本号:"+atomicStampedReference.getStamp()
                    +"\t当前实际最新值:"+atomicStampedReference.getReference());
        },"t4").start();
    }
}

java_CAS

上一篇:对死锁的理解


下一篇:Linux学习之让进程在后台可靠运行的方法详解