【并发编程】CAS与FAA

在并发编程中,原子性操作无处不在,不管程序中是怎么实现原子性操作,底层都是通过CPU的指令来实现的,这里来介绍一下最著名的:CAS与FAA!

CAS: 比较并交换(compare and swap, CAS),是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。 该操作通过将内存中的值与指定数据进行比较,当数值一样时将内存中的数据替换为新的值。

在应用中CAS可以用于实现无锁数据结构,常见的有无锁队列(先入先出)[3] 以及无锁堆(先入后出)。对于可在任意位置插入数据的链表以及双向链表,实现无锁操作的难度较大

ABA问题是无锁结构实现中常见的一种问题,可基本表述为:

  • 进程P1读取了一个数值A
  • P1被挂起(时间片耗尽、中断等),进程P2开始执行
  • P2修改数值A为数值B,然后又修改回A
  • P1被唤醒,比较后发现数值A没有变化,程序继续执行。

对于P1来说,数值A未发生过改变,但实际上A已经被变化过了,继续使用可能会出现问题。在CAS操作中,由于比较的多是指针,这个问题将会变得更加严重。

Java中CAS是通过sun.misc.Unsafe提供了compareAndSwap等方法实现的

CAS操作是通过CPU的lock:cmpxchg等指令实现原子性操作的!

FAA: fetch-and-add是CPU指令(FAA),对内存位置执行增加一个数量的原子操作

x = x+a  //令x 变为x + a,其中x是个内存位置,a是个值

FAA可用于实现互斥锁(mutex locks)、信号量(semaphores)。

x86实现
从8086起,以内存为目的操作数的ADD指令就是fetch-and-add。如果使用LOCK前缀,那么它对多处理器是原子操作。但不能返回原值,直至486引入XADD指令。

下述GCC编译的C语言函数,在x86的32位与64位平台上,使用扩展asm语法:

  static inline int fetch_and_add(int* variable, int value)
  {
      __asm__ volatile("lock; xaddl %0, %1"
        : "+r" (value), "+m" (*variable) // input+output
        : // No input-only
        : "memory"
      );
      return value;
  }

参考:
https://zh.wikipedia.org/wiki/比较并交换
https://zh.wikipedia.org/wiki/Fetch-and-add

你可能感兴趣的:(Java并发,CAS,FAA,cmpxchg,XADD)