CAS

为什么要上锁?锁的本质是什么?

上锁的本质其实就是为了让资源能被正常的修改。为什么不上锁就不行呢?因为在JMM中,存在

着共享内存以及线程私有内存的,线程不是总会把私有内存里的资源立马加载到共享的,存在着时间

差,也就导致了覆盖的问题,此时就是我们常说的线程不安全。这时候我们使用ReentrantLock or · synchronized去包住一段代码,保证 同一时间内只会有一个线程去操作里面的资源,即使不会立马同步

到共享内存,也不会有其他线程来竞争。而且只要代码执行出了锁的范围,就会立马同步到主存

这里的资源是指堆内的,即成员变量

可以看到synchronized和ReentrantLock锁的是比较大的范围,容易误伤

所以我们可以把锁的范围降低,同时并不总是一直会有 对共享资源的访问和修改,我们可以基于一种乐观的

思想,比较资源的现有值和预期值,如果一致,说明没有人访问过,那么我直接修改

预期值和现有值是指什么?

比如一个对象里有个字段int a,我之前读出来,读出a是2,这个2就是预期值,那么我需要把他加10,更改为12,此时要更改了,我就看他的现有的实际的值,如果还是预期值2,说明在我读出来,再做运算,再到现在打算更改这个过程中,没有其它线程来修改,那么我就可以把它改为12。如果不是2,例如变成5了,那么实际值5,就和预期值2不一样了, 说明这段时间内有其它线程对其修改了,那我就不能动他,否则就产生覆盖了,因为我的12是基于2来计算出来的

即 一起竞争,谁快谁有理

CAS compare And swap 正是这样的锁,其实不只是锁了,更能说是一种思想。但其实也能说是一种锁,毕竟比较和交换这两步得是原子的。

cas基于cpu的一个原子指令来使其原子操作

cmp x chg:cpu执行这条指令时,会自动锁住总线,防止其他cpu访问共享变量;cpu同时自动禁止中断

同时硬件会保证对共享变量的访问是原子的

CAS存在的问题:

ABA,但说实在的业务上根本没有这个问题

CAS与悲观锁的区别:

  • 粒度不同,CAS的粒度是针对一个变量的修改, 悲观锁的粒度是一段代码块
  • 思想不同,CAS是乐观的思想,失败了大不了再重试,悲观锁是悲观的思想,我就笃定会有其它线程干扰,直接上锁
  • 场景不同,CAS适用读多写少,悲观锁适用读少写多
  • 开销不同,CAS开销小,悲观锁开销大

有了CAS为什么还要volatile?

CAS只是原子修改,并不能保证可见性,修改完后,其它线程并不一定马上能看到最新值