CAS自旋

1. CSA简介

比较并交换(compare and swap, CAS),是原子操作的一种。在多线程没有锁的状态下,可以保证多个线程对同一个值的更新。

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

2. 自旋–比较和交换

自旋: 就是不停的判断比较,看能否将值交换

我们都知道,多个线程在访问共享资源的时候,会产生同步问题,所以需要加锁来保证安全。但是,一旦加了锁,同一时刻只能有一个线程获取锁对象,效率自然变低了。

那在多线程场景下,不加锁的情况下来修改值,CAS是怎么自旋的呢?

CAS自旋

【用图举例来说明】

  • 现在Data中存放的是num=0,线程A将num=0拷贝到自己的工作内存中计算(做+1操作)E=0,计算的结果为V=1
  • 由于是在多线程不加锁的场景下操作,所以可能此时num会被别的线程修改为其他值。此时需要再次读取num看其是否被修改,记再次读取的值为N
  • 如果被修改,即E != N,说明被其他线程修改过。那么此时工作内存中的E已经和主存中的num不一致了,根据EMSI协议,保证安全需要重新读取num的值。直到E = N才能修改
  • 如果没被修改,即E = N,说明没被其他线程修改过。那门将工作内存中的E=0改为E=1,同时写回主存。将num=0改为num=1

这就是自旋的比较和交换:

在多线程没有锁的状态下,可以保证多个线程访问对某一个值的更新。

3. 什么是ABA问题

举个不恰当的例子????。张三和他的女朋友李四分手了,经历了全球变暖以及黄金的贬值的重重考验和磨难后最终又复合。那么,在分手期间,张三是不知道李四有没有重新找过男朋友的。但是,最终的男盆友是自己就行了!这就是ABA问题。

回归到问题本身:

在线程A计算V的时候,可能线程B将num=0改为了num=2,线程C又将num=2改回了num=0(也有可能是线程B或这任意线程)。此时num的值虽然还时0,但是num已经不是一开始的num了。

用一句哲学的话来讲,就是:“世界上没有两片相同的树叶”!

4. ABA问题怎么解决

大家在更新手机APP的时候,每次更新时软件都会有版本号。你更新完软件,还是原来的应用,但是版本号就不一样了。

同样,ABA问题也可以这样来求解。

我们把值num加一个版本号tag,当有线程修改的时候,版本号就会发生变化。在读取E的时候,同时将版本号tag也读取上,在比较E=?=N的时候,同时比较tag是否发生了改变。

5. 悲观锁

总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。在Java中,synchronized的思想就是悲观锁。

6. 乐观锁

总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁。

乐观锁总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁。

但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。java.util.concurrent包中借助CAS实现了区别于synchronized同步锁的一种乐观锁。