CAS JUCJava并发编程
对于并发控制来说,锁是一种悲观策略。他总是假设每一次临界区的操作会产生冲突,所以就能可让线程等待,也要先获取锁,那么这样回阻塞线程的执行,所以怎么改进呢? 比较交换技术(CAS Compare And Swap).
CAS
它的思想其实很简单。它包含三个参数CAS(V,E,N).V表示要更新的变量,E表示预期值,N表示新值。
仅仅当V==E是,才会将V的值设置成N,如果V和E不相同,那么说明有其他线程进行了更新,当前线程什么都不做。当有多个线程同时使用CAS的来操作一个变量的时候,只有一个能够成功更新。其他的则会失败,并且可以一直尝试,这样即使并发操作一个变量没有加锁,也能够发现其他线程对于当前线程的干扰。
讲人话:就是你有一个期望值,如果读取的值和期望值不一样,说明被改变了,那么就重新读取,尝试再次修改。
那么来看一个源码:
AtomicInteger
方法有这些。
字段有:
private volatile int value;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; //偏移量
那么来看看他增加操作
跳到Unsafe类中看看:
此处几个变量进行说明:
var1实际上是当前的对象
var2是当前值
var5是底层取出来的值
var4是需要增加的值
public native int getIntVolatile(Object var1, long var2);//获取当前变量真实的值
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
我们可以看到compareAndSwapInt方法是一个native方法,其主要功能是做到的是如果var2==var5那么就将var5+var4赋值给该对象,如果不等于就循再次获得var2和var5进行比较,直到当前值和期望值相同,进行跟新。
compareAndSwapInt的思想其实就是CAS的思想核心。
至于其他的Atomic Variables就差不多
ABA问题
那么问题来了,在CAS执行比较并且替换的这段时间,会不会发生数据的更改呢?
其实是有可能被其他线程更改的,因为我们没有加锁。
比如说一个线程1从存储位置V取出A,另一个线程2也是取出A,并且2将num变成了B,然后线程2又将他变成A,我们在线程1进行操作的时候会发现,V位置上是A,尽管会成功,但是会发生什么呢?
看个例子:
小明在提款机,提取了50元,因为提款机问题,有两个线程,同时把余额从100变为50
线程1(提款机):获取当前值100,期望更新为50,
线程2(提款机):获取当前值100,期望更新为50,
线程1成功执行,线程2某种原因block了,这时,某人给小明汇款50
线程3(默认):获取当前值50,期望更新为100,
这时候线程3成功执行,余额变为100,
线程2从Block中恢复,获取到的也是100,compare之后,继续更新余额为50!!!
此时可以看到,实际余额应该为100(100-50+50),但是实际上变为了50(100-50+50-50)这就是ABA问题带来的成功提交。
解决办法:部分乐观锁是通过版本号来决绝这个问题的,在进行数据操作的时候都会加上个版本号,如果版本号和期望的一样,就可以进行操作,否则执行失败。
上一篇: Spring boot 无端口模式启动
下一篇: 并发编程之CAS(二)