【多线程】CAS乐观锁

CAS(Compare and Swap)

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

CAS如何实现线程安全

线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。这个过程是原子的。我们利用图来更加直观的理解一下。
【多线程】CAS乐观锁_第1张图片

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乐观锁_第2张图片

线程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乐观锁_第3张图片

你可能感兴趣的:(多线程,Java,java)