逐梦少年,看你能不能发现宝藏( 十 )


4.这时线程A恢复,执行方法比较,发现自己手里的值数字3和主内存的值数字4不一致,说明该值已经被其它线程抢先一步修改过了,那A线程本次修改失败,只能重新读取重新来一遍了 。
5.线程A重现获取value值,因为变量value值被修饰,所以其它线程对它的修改,线程A总是能够看到,线程A继续执行 进行比较替换,直到成功 。
小总结:
? CAS()比较 当前工作内存中的值,和主内存中的值,如果相同则执行规定操作,否则继续比较直到主内存和工作内存中的值一致为止 。
? CAS应用,CAS有3个操作数,内存值V,旧的预期值A,要修改的更新值B 。
? 当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做 。
--》 CAS–》–》CAS底层思想–》ABA–》原子引用更新–》如何规避ABA问题
CAS的缺点:
? 1.循环时间长开销大(我们可以看到方法执行时,有个do while),
public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;}
如果CAS失败,会一直尝试,如果CAS长时间一直不成功,可能会给CPU带来很大的开销 。
? 2.只能保证一个共享变量的原子操作
? 对于一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操纵,
? 但是,
? 对多个共享变量操作时(像加锁,我们可以保证一段代码),循环CAS就无法保证操作的原子性,这个时候就可以用锁 来保证原子性 。
? 3.引来ABA问题?(原子类的ABA问题谈谈?原子更新引用知道么?)
? ABA:狸猫换太子
? CAS会导致“ABA问题” 。
? CAS算法势下一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个事件差类会导致数据的变化 。
? 比如说一个线程one从内存位置V取出A,这个时候,另一个线程two也曾那个内存中取出A,并且线程two进行了一些操作将值变成了B,然后线程
? two又将V位置的数据变成A,这时候线程one女性CAS操作发现内存中仍然是A,然后线程one操作成功 。
? 尽管线程one的CAS操作成功,但不代表这个线程就是没有问题的 。
解决ABA问题???理解原子引用 + 新增一种机制,那就是修改版本号(类似时间戳)
? 原子引用
@Getter@ToString@AllArgsConstructorclass User {String userName;intage;}public class AtomicReferenceDemo {public static void main(String[] args) {User z3 = new User("z3",22);User li4 = new User("li4",25);AtomicReference atomicReference = new AtomicReference<>();atomicReference.set(z3);System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.get().toString());}}==================================================true User(userName=li4, age=25)false User(userName=li4, age=25)
? 时间戳的原子引用
//ABA问题的解决AtomicstampedRefencepublic class ABADemo {static AtomicReference reference= newAtomicReference(100);static AtomicStampedReference atomicStampedReference = new AtomicStampedReference<>(100,1);public static void main(String[] args) {System.out.println("=====================以下时ABA问题的产生====================");new Thread(()->{reference.compareAndSet(100,101);reference.compareAndSet(101,100);},"t1").start();new Thread(()->{//暂停一秒钟t2线程,保证t1下次呢很难过完成了1次ABA操作 。try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(reference.compareAndSet(100, 2018)+"\t"+reference.get());},"t2").start();System.out.println("=====================以下时ABA问题的解决====================");new Thread(()->{int stamp = atomicStampedReference.getStamp();System.out.println( Thread.currentThread().getName()+"\t第一次版本号" +atomicStampedReference.getStamp());//暂停一秒钟t3线程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);//暂停一秒钟t3线程try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}boolean b = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);System.out.println(Thread.currentThread().getName()+"\t修改成功否:"+b+"当前最新时间版本号"+atomicStampedReference.getStamp());System.out.println(Thread.currentThread().getName()+"\t当前实际最新值:"+atomicStampedReference.getReference());},"t4").start();}}=========================================================================================以下时ABA问题的产生=========================================以下时ABA问题的解决====================t3 第一次版本号1t4 第一次版本号1t3 第二次版本号2true 2018t3 第三次版本号3t4 修改成功否:false当前最新时间版本号3t4 当前实际最新值:100