【多线程】常常听到的CAS是什么呢,有什么作用呢

CAS,全称是Compare And Swap,是比较并交换的意思
CAS是⼀种原⼦操作,它是⼀种系统原语,是⼀条CPU的原⼦指令,从CPU层⾯保证它的原⼦性

1、CAS的过程

V:要更新的变量(var)
E:预期值(expected)——本质上指的是“旧值”
N:新值(new)
判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。
当多个线程同时使⽤CAS操作⼀个变量时,只有⼀个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。

举个栗子
有一个多线程共享的变量var等于1,线程A想设置var为2。使用CAS实现
首先var与1对比,发现它等于1,说明var没有被别的线程更改过,那就把var设置为新的值2,此次CAS成功,var的值就设置成了2
如果对比发现不等于1,说明var被其它线程更改过了(比如var是3了),就什么也不做,此次CAS失败,var的值还是3

2、Java实现CAS的原理

在Java中,有⼀个 Unsafe 类,⾥⾯是⼀些 native ⽅法,就是CAS的。

Java中,如果⼀个⽅法是native的,那Java就不负责具体实现它,⽽是交给底层的JVM使⽤c或者c++去实现

Java中对CAS的实现是C++写的,它的具体实现和操作系统、CPU都有关系。Linux的X86下主要是通过 cmpxchgl 这个指令在CPU级完成CAS操作的,但在多处理器情况下必须使⽤ lock 指令加锁来完成。当然不同的操作系统和处理器的实现会有所不同。

3、CAS的作用

可以使用循环CAS实现原子操作
利用了处理器提供的CMPXCHG指令实现的,自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止

CAS实现原子操作的三大问题

CAS虽然可以很高效地解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大,以及只能保证一个共享变量的原子操作。

1、ABA问题

CAS需要在操作值的时候,检查值有没有发生变化,如果没有发生变化则更新
如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了
解决思路
在变量前⾯追加上版本号或者时间戳,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A

2、循环时间长开销大

CAS多与⾃旋结合。如果⾃旋CAS⻓时间不成功,会占⽤⼤量的CPU资源。
解决思路
让JVM⽀持处理器提供的pause指令,效率会有一定的提升
pause指令能让⾃旋失败时cpu睡眠⼀⼩段时间再继续⾃旋,从⽽使得读操作的频
率低很多,为解决内存顺序冲突⽽导致的CPU流⽔线重排的代价也会⼩很多。

3、只能保证⼀个共享变量的原子操作

当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性
解决思路
(1)使⽤锁。锁内的临界区代码可以保证只有当前线程能操作
(2)把多个共享变量合并成一个共享变量来操作
从Java 1.5开始,JDK就提供了AtomicReference 类来保证引用对象之间的原⼦性,把多个变量放到⼀个对象⾥⾯进⾏CAS操作;

Java实现原子操作的两种方式:循环CAS实现原子操作,使用锁机制实现原子操作

你可能感兴趣的:(多线程,CAS,多线程,共享变量,原子操作)