1.什么是 CAS CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术。简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class CASDemo { public static void main (String[] args) { AtomicInteger atomicInteger = new AtomicInteger (2020 ); System.out.println(atomicInteger.compareAndSet(2020 , 2021 )); System.out.println(atomicInteger.get()); atomicInteger.getAndIncrement() System.out.println(atomicInteger.compareAndSet(2020 , 2021 )); System.out.println(atomicInteger.get()); } }
Unsafe 类
CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
缺点:
1、 循环会耗时
2、一次性只能保证一个共享变量的原子性
3、ABA问题
2.CAS导致的ABA问题
ABA问题:A改为B,B又改回A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class CASDemo { public static void main (String[] args) { AtomicInteger atomicInteger = new AtomicInteger (2020 ); System.out.println(atomicInteger.compareAndSet(2020 , 2021 )); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2021 , 2020 )); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2020 , 6666 )); System.out.println(atomicInteger.get()); } }
3.原子引用
解决ABA 问题,引入原子引用! 对应的思想:乐观锁!
带版本号 的原子操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public class CASDemo { static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference <>(1 ,1 ); 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(); } 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(); } }
注意:
Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间。