CAS,即Compare and Swap,中文翻译为“比较并交换”。java
对于JUC包中,CAS理论是实现整个java并发包的基石。从总体来看,concurrent包的实现示意图以下:算法
i++是一个很是经典的操做,它几乎充斥着咱们每一个人编写的代码中。咱们知道i++是能够分解的,它分解为getI()、i + 1 、setI三个步骤,因此它并非原子操做。若是i==1,执行两次i++操做,咱们指望的结果是3,可是结果有可能也是2:编程
那么有什么办法解决这个问题呢?确定有!使用锁便可:多线程
synchronized(this){ i++; }
诚然,在java中存在乐观锁、悲观锁两种锁。其中synchronized就是悲观锁,在前面咱们了解synchronized也是独占锁,加入关键字synchronized的代码通常都是以单线程的形式在运行着,它会致使其余须要该资源的线程挂起直到前面的线程执行完毕释放资源,因此它的效率较为低下。而乐观锁则采用了一种较为高效的方式,它的操做与synchronized不一样,synchronized采用加锁,而它则不采用加锁去执行某些操做,若是发生了冲突则失败并一直重试直到成功为止。而CAS就是一种乐观锁,它所采用的策略是当且仅当预期值A和存中的值V相同,则将内存V值修改成B,不然返回V。实现以下:并发
for(;;){ if(A==V){ V = B; } }
固然在J.U.C中实现CAS没有这么简单。this
CAS,即一种对内存中的共享数据进行操做的指令,并且该操做是原子的读写操做。其过程以下:首先CPU将内存中的将要被修改的数据与预期的值进行比较,若是这两个值相等,CPU则会将内存中数值替换为新值,不然不作操做。最后,CPU会将旧值返回。在java中,CAS的含义就是“我认为的本来的值是什么,若是你是,则更换为新值,不然不作修改同时麻烦告诉我该值时多少”。spa
在CAS中,总共存在三个操做数:预期值A、内存中的V、修改的值B。当且仅当预期值A和内存中的值V相同,则将内存V值修改成B,不然返回V。使用这种机制编写的算法也叫做非阻塞算法,标准定义了一个线程的失败或者挂起是不会影响其余线程的失败或者挂起。.net
下面咱们来已AtomicIneger的源码为例来看看CAS操做:线程
public final int getAndAdd(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return current; } }
这里很显然使用CAS操做(for(;;)里面),他每次都从内存中读取数据,+1操做,而后两个值进行CAS操做。若是成功则返回,不然失败重试,直到修改为功为止。翻译
上面源码最关键的地方有两个,一个for循环,它表明着一种宁死不屈的精神,不成功誓不罢休。还有就是compareAndSet:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
尽管CAS机制可使咱们不依赖与同步,不影响和挂起其余线程,它大大提高了运行的效率,可是它会致使一个ABA的问题,以下:加入有两个线程A、B,他们都读取内存中的数据V,假如这个时候线程A,先将V修改成V1,而后又修改成V,这个时候线程B的compareAndSet仍然能成功,对于线程B而言该值V并无发生任何变化,而实际上它已经变化了,只不过最后又还原了而已。
参考文献