CAS(Compare and swap)
乐观锁和悲观锁
乐观锁和悲观锁只是两种设计思想,和语言无关,并不是java独有的东西。
乐观锁:同一个时间点,经常只有一个线程来操作共享变量。(总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。)
悲观锁:同一个时间点,经常有多个线程在操作共享变量。(总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁)
乐观锁的原理:
直接操作共享变量(不会阻塞线程)
加锁的方式:直接加锁,要么直接加锁成功,要么直接加锁失败(线程安全的操作)
通过api调用方法的返回值,知道是成功还是失败。
两种思想的实现
悲观锁实现:synchronized
和ReentrantLock
等独占锁就是悲观锁思想的实现。
乐观锁实现:java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
CAS是什么
CAS——CompareAndSwap——比较并交换
CAS包含三个操作数——内存位置(V)、预期值(A)、拟写入的新值(B)
第一步:比较内存位置V中的值和预期值A是否相等
第二步:如果相等,就把拟写入的新值B写入内存位置V ,否则不做任何操作,继续尝试cas
第三步:返回boolean类型值,表示操作是否成功
当多个线程同时对某个资源进行CAS操作的时候,只能有一个线程操作成功,其他线 程会自旋等待。
缺陷
ABA问题
ABA问题就是V中的这个值从A变成了B,又从B变回了A,这个变化的过程我们 并不知道,也就是在线程执行CAS操作的时候,有其他线程修改了内存中的变量 值A->B->A。
解决方案:引入版本号(例如AtomicStampedReference(原子类)类就提供了这一功能的支持)
其他缺陷
1.循环时间开销过大
对于资源竞争严重的情况,CAS自旋的概率会比较大,从而浪费了很多的CPU资 源,效率低于synchronized。
2.只能保证一个共享变量的原子性操作
对于一个共享变量执行的时候,可以使用CAS的方式来保证原子性,但是对多个共享变量进行操作的时候,循环CAS就无法保证操作的原子性了,需要通过加锁来实现。
CAS底层实现原理
JAVA中的CAS操作都是通过sun包下Unsafe类实现,而Unsafe类中的方法都是native方法,由JVM本地实现,所以最终的实现是基于C、C++在操作系统之上的。本质上就是基于CPU提供的原子性线程安全的修改操作。