Skip to content

Latest commit

 

History

History
94 lines (43 loc) · 2.65 KB

File metadata and controls

94 lines (43 loc) · 2.65 KB

CAS

一、基本概念

讲到 CAS 不论是 swap 还是 set ,肯定都是属于并发内容

简单说一下 compareAndSet

CAS 的核心函数 CAS(V, E, N)

  • V 代表准备更新的变量,共享变量
  • E 代表我们所期望的值,比如 0
  • N 代表我们准备修改的值,比如 1

在并发中,这种操作,相当于加了一把锁,并无涉及到上下文的切换,内核态用户态的转变,只是做了一个比较

而这个 CAS 操作,在你看来涉及到了取值比较设值 操作这么多操作,看似好像有模有样,但是会不会本身就是有线程安全问题的呢?

其实 CAS 是属于操作系统层次的一个原语是一条 CPU 的原子指令,我们使用 CAS 不是自己来实现的,而是通过操作系统、语言暴露出来的接口来实现

比如说 Unsafe 类的操作


现在,我们总结一下上面内容

  1. CAS 是一个无锁操作(无使用 Sychronized 等重量级锁)
  2. CAS 是一个原子指令

我们通常并发情况下回涉及到线程的竞争,这句话来说有点官方,简单理解就是并发会涉及到到底是哪个线程有资格来操作?

看下两个线程模拟这个 CAS 操作

第一轮:

ThreadA -> 进行 CAS -> 发现符合期望,设置我们期望的值(向外界宣告:有线程在正在操作)
ThreadB -> 进行 CAS -> 发现不符合期望(已经被线程A修改了) -> 停止操作

二、自旋

再看这个

第二轮:

ThreadA -> 执行完任务后,将值恢复成原来的样子        
ThreadB -> 反复循环 while(true) 不断尝试 CAS 操作 -> 直到 CAS 操作成功

这个第二轮的循环就是自旋


我们通常在循环中经常会用到 i++ 操作,而这个操作不是原子的,JDK 为我们提供比较完善的解决方案,那就是 Atomic 包下面的内容,其中大部分都是使用 CAS 操作


为什么使用自旋

举个例子,你,(内核态),厕所(线程)

你内急去上厕所,发现厕所已经有人了,你会怎么做?

  1. 等别人上完厕所
  2. 重新再去找一个厕所

在短时间,我们预料到那个人很快就能上完厕所,我只需要再等一会儿就行了,没有必要重新找,重新找就太麻烦了,明显成本更高

所以我们等待的这个操作就叫做自旋

才计算机中也是同样的道理,某个线程在执行程序的时候,我们判断八成很快就能执行完,所以我们原地自旋,这就是自旋。如果反复自旋,里面的线程始终不释放锁,我们可以进行锁粗化。