【多线程】CAS乐观锁
程序员文章站
2022-07-10 19:18:52
CAS(Compare and Swap 比较并替换),是一条CPU并发原语。它是乐观锁的一种实现方式,也是一种轻量级锁。...
CAS(Compare and Swap)
CAS(Compare and Swap 比较并替换),是一条CPU并发原语。它是乐观锁的一种实现方式,也是一种轻量级锁。
CAS如何实现线程安全
线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。这个过程是原子的
。我们利用图来更加直观的理解一下。
public class CASDemo{
public static void main(string[] args){
AtomicInteger atomicInteger = new AtomicInteger(initialValue: 5);
// main do thing......
System.out.println(atomicInteger.compareAndSet(expect: 5, update: 2020) + "\t current data: " + atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(expect: 5, update: 1024) + "\t current data: " + atomicInteger.get());
}
}
这个过程中依然存在问题:
1 循环时间长,开销很大。
如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能会给CPU带来很大的开销。
2 只能保证一个共享变量的原子操作。
CAS操作单个共享变量的时候可以保证原子的操作,多个变量就不行了,JDK 5之后 AtomicReference可以用来保证对象之间的原子性,就可以把多个对象放入CAS中操作。
拿 AtomicInteger
举例,它的自增函数 incrementAndGet ()
就是这样实现的,其中就有大量循环判断过程,直到符合条件才成功。
public final int getAndAddInt(Object var1, long var2, int var4){
int var5;
do{
var5 = this.getIntVolatile(var1, var2);
}while(!this.compareAndSwapInt(var1, var2, var5, var5:var5 + var4));
return var5;
}
大概意思就是循环判断给定偏移量是否等于内存中的偏移量,直到成功才退出。
3 ABA问题
执行顺序:
线程1读取了数据A
线程2读取了数据A
线程2通过CAS比较,发现值是A没错,可以把数据A改成数据B
线程3读取了数据B
线程3通过CAS比较,发现数据是B没错,可以把数据B改成了数据A
线程1通过CAS比较,发现数据还是A没变,就写成了自己要改的值
在这个过程中任何线程都没做错什么,但是值被改变了,线程1却没有办法发现,其实这样的情况出现对结果本身是没有什么影响的,但是我们还是要防范。
如何防范ABA问题?
- 加标志位。例如添加一个自增字段,操作一次就自增加一。
- 时间戳。比较时间戳的值
未加版本号:
// 之前未加版本号的版本
update table set value = newValue where value = #{oldValue}
//oldValue就是我们执行前查询出来的值
加了版本号:
// 加了版本号之后
update table set value = newValue ,vision = vision + 1 where value = #{oldValue} and vision = #{vision}
// 判断原来的值和版本号是否匹配,中间有别的线程修改,值可能相等,但是版本号100%不一样
本文地址:https://blog.csdn.net/Xumuyang_/article/details/107681195