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

CAS JUCJava并发编程

程序员文章站 2022-05-04 12:51:31
...

对于并发控制来说,锁是一种悲观策略。他总是假设每一次临界区的操作会产生冲突,所以就能可让线程等待,也要先获取锁,那么这样回阻塞线程的执行,所以怎么改进呢? 比较交换技术(CAS Compare And Swap).

CAS

它的思想其实很简单。它包含三个参数CAS(V,E,N).V表示要更新的变量,E表示预期值,N表示新值。
仅仅当V==E是,才会将V的值设置成N,如果V和E不相同,那么说明有其他线程进行了更新,当前线程什么都不做。当有多个线程同时使用CAS的来操作一个变量的时候,只有一个能够成功更新。其他的则会失败,并且可以一直尝试,这样即使并发操作一个变量没有加锁,也能够发现其他线程对于当前线程的干扰。

讲人话:就是你有一个期望值,如果读取的值和期望值不一样,说明被改变了,那么就重新读取,尝试再次修改。

那么来看一个源码:
AtomicInteger
CAS JUCJava并发编程
方法有这些。
字段有:

private volatile int value;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; //偏移量

那么来看看他增加操作
CAS JUCJava并发编程

跳到Unsafe类中看看:
CAS JUCJava并发编程
此处几个变量进行说明:
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问题带来的成功提交。

解决办法:部分乐观锁是通过版本号来决绝这个问题的,在进行数据操作的时候都会加上个版本号,如果版本号和期望的一样,就可以进行操作,否则执行失败。