CAS 全称是 compare and swap,是一种用于在多线程环境下实现同步功能的机制。java
CAS 它是一条CPU并发原语。操做包含三个操做数 -- 内存位置、预期数值和新值。CAS 的实现逻辑是将内存位置处的数值与预期数值想比较,若相等,则将内存位置处的值替换为新值。若不相等,则不作任何操做。这个过程是原子的。多线程
CAS并发原语体如今java语言中的sun.misc.Unsafe类中的各个方法。调用Unsafe类中的CAS方法,JVM会帮咱们实现汇编指令。这是一种彻底依赖硬件的功能,经过它实现了原子操做。因为CAS是一种系统原语,原语属于操做系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,而且原语的执行必须是连续的,在执行过程当中不容许被打断,也就是说CAS是一条CPU的原子指令,不会形成所谓的数据不一致问题。并发
Unsafe类是CAS的核心类,因为Java方法没法直接访问底层系统,须要经过本地(native)方法来访问,基于该类能够直接操做特定内存的数据。Unsafe类存在与sum.misc包中,其内部方法操做能够像C的指针同样直接操做内存,由于Java中CAS操做的执行依赖于Unsafe类的方法。源码分析
Unsafe类中的全部方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操做系统底层资源执行相应任务。优化
public class CASDemo { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(5); // 运行结果: true 2019 System.out.println(atomicInteger.compareAndSet(5, 2019) + "\t" + atomicInteger.get()); // 运行结果: false 2019 System.out.println(atomicInteger.compareAndSet(5, 1024) + "\t" + atomicInteger.get()); // 此方法能够解决多线程环境下i++问题,底层使用的是Unsafe类CAS和自旋锁 atomicInteger.getAndIncrement(); } }
源码分析:this
/** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { // this=当前对象 valueOffset=内存偏移量(内存地址) 1=固定值,每次调用+1 // Unsafe就是根据内存偏移地址获取数据的。 return unsafe.getAndAddInt(this, valueOffset, 1); } /** * 为了方便查看和添加注释,此方法是从Unsafe类中复制出来的 */ public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { // 获取var1对象,内存地址在var2的值。 // 至关于这个线程从主物理内存中取值copy到本身的工做内存中。 var5 = this.getIntVolatile(var1, var2); // 比较并交换,若是var1对象,内存地址在var2的值和var5值同样,那么就+1 // compareAndSwapInt若是返回true,取反为false,说明更新成功,退出循环,则返回。 // compareAndSwapInt若是返回false,取反为true,说明当前线程工做内存中的值和主物理内存中的值不同,被其余线程修改了,则继续循环获取比较,直到更新成功为止。 } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
执行过程说明:atom
volatile简单说明:
volatile是一个轻量级的同步机制, 三大特性: 保证可见性, 不保证原子性, 禁止指令重排。操作系统
优势:线程
缺点:指针
举个栗子说明:
主内存有个数据值:A,两个线程A和B分别copy主内存数据到本身的工做区,A执行比较慢,须要10秒, B执行比较快,须要2秒, 此时B线程将主内存中的数据更改成B,过了一会又更改成A,而后A线程执行比较,发现结果是A,觉得别人没有动过,而后执行更改操做。其实中间已经被更改过了,这就是ABA问题。
也就是ABA问题只要开始时的数据和结束时的数据一致,我就认为没改过,无论过程。
尽管A线程的CAS操做是成功的,可是不表明这个过程就是没问题的。
ABA问题说简单点就是,预判值仍是和当初抓取的同样,可是这个“ 值 ”的版本可能不同了,在某些不只要考虑数据值是否一致,还要考虑版本是否一致的场景下须要注意.
Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它能够经过控制变量值的版原本保证CAS的正确性。
/** * 解决CAS的ABA问题 */ public class SolveABAOfCAS { static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1); public static void main(String[] args) throws InterruptedException { System.out.println("==========如下是ABA问题的产生=========="); new Thread(() -> { atomicReference.compareAndSet(100, 101); atomicReference.compareAndSet(101, 100); }, "t1").start(); new Thread(() -> { try { // 暂停1秒钟,保证上面完成一次ABA操做 Thread.sleep(1000); System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get()); } catch (InterruptedException e) { e.printStackTrace(); } }, "t2").start(); Thread.sleep(2000); System.out.println("==========如下是ABA问题的解决=========="); new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName() + "\t第1次版本号" + stamp); try { // 暂停一秒钟t3线程 Thread.sleep(1000); atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName() + "\t第2次版本号" + atomicStampedReference.getStamp()); atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName() + "\t第3次版本号" + atomicStampedReference.getStamp()); } catch (InterruptedException e) { e.printStackTrace(); } }, "t3").start(); new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName() + "\t第1次版本号" + stamp); try { // 暂停3秒钟t4线程,保证上面的t3线程完成一次ABA操做 Thread.sleep(3000); boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1); System.out.println(Thread.currentThread().getName() + "\t修改为功否: " + result + "\t当前最新实际版本号: " + atomicStampedReference.getStamp()); System.out.println(Thread.currentThread().getName() + "\t当前实际最新值: " + atomicStampedReference.getReference()); } catch (InterruptedException e) { e.printStackTrace(); } }, "t4").start(); } }
若是以为对你有帮助,欢迎来访个人博客:http://jianjieming.com