欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

【多线程】CAS乐观锁

程序员文章站 2022-07-10 19:18:52
CAS(Compare and Swap 比较并替换),是一条CPU并发原语。它是乐观锁的一种实现方式,也是一种轻量级锁。...

CAS(Compare and Swap)

CAS(Compare and Swap 比较并替换),是一条CPU并发原语。它是乐观锁的一种实现方式,也是一种轻量级锁。

CAS如何实现线程安全

线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。这个过程是原子的。我们利用图来更加直观的理解一下。
【多线程】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问题

执行顺序:【多线程】CAS乐观锁

线程1读取了数据A

线程2读取了数据A

线程2通过CAS比较,发现值是A没错,可以把数据A改成数据B

线程3读取了数据B

线程3通过CAS比较,发现数据是B没错,可以把数据B改成了数据A

线程1通过CAS比较,发现数据还是A没变,就写成了自己要改的值

在这个过程中任何线程都没做错什么,但是值被改变了,线程1却没有办法发现,其实这样的情况出现对结果本身是没有什么影响的,但是我们还是要防范。

如何防范ABA问题?
  1. 加标志位。例如添加一个自增字段,操作一次就自增加一。
  2. 时间戳。比较时间戳的值

未加版本号:

// 之前未加版本号的版本
update table set value = newValue where value = #{oldValue}
//oldValue就是我们执行前查询出来的值 

加了版本号:

// 加了版本号之后
update table set value = newValue ,vision = vision + 1 where value = #{oldValue} and vision = #{vision} 
// 判断原来的值和版本号是否匹配,中间有别的线程修改,值可能相等,但是版本号100%不一样

                               【多线程】CAS乐观锁

本文地址:https://blog.csdn.net/Xumuyang_/article/details/107681195

相关标签: 多线程 Java