讲到 CAS 不论是 swap 还是 set ,肯定都是属于并发内容
简单说一下 compareAndSet
CAS 的核心函数 CAS(V, E, N)
V代表准备更新的变量,共享变量E代表我们所期望的值,比如 0N代表我们准备修改的值,比如 1
在并发中,这种操作,相当于加了一把锁,并无涉及到上下文的切换,内核态用户态的转变,只是做了一个比较
而这个 CAS 操作,在你看来涉及到了取值、比较、设值 操作这么多操作,看似好像有模有样,但是会不会本身就是有线程安全问题的呢?
其实 CAS 是属于操作系统层次的一个原语,是一条 CPU 的原子指令,我们使用 CAS 不是自己来实现的,而是通过操作系统、语言暴露出来的接口来实现
比如说 Unsafe 类的操作
现在,我们总结一下上面内容
- CAS 是一个无锁操作(无使用 Sychronized 等重量级锁)
- CAS 是一个原子指令
我们通常并发情况下回涉及到线程的竞争,这句话来说有点官方,简单理解就是并发会涉及到到底是哪个线程有资格来操作?
看下两个线程模拟这个 CAS 操作
第一轮:
ThreadA -> 进行 CAS -> 发现符合期望,设置我们期望的值(向外界宣告:有线程在正在操作)
ThreadB -> 进行 CAS -> 发现不符合期望(已经被线程A修改了) -> 停止操作
再看这个
第二轮:
ThreadA -> 执行完任务后,将值恢复成原来的样子
ThreadB -> 反复循环 while(true) 不断尝试 CAS 操作 -> 直到 CAS 操作成功
这个第二轮的循环就是自旋
我们通常在循环中经常会用到 i++ 操作,而这个操作不是原子的,JDK 为我们提供比较完善的解决方案,那就是 Atomic 包下面的内容,其中大部分都是使用 CAS 操作
举个例子,你,(内核态),厕所(线程)
你内急去上厕所,发现厕所已经有人了,你会怎么做?
- 等别人上完厕所
- 重新再去找一个厕所
在短时间,我们预料到那个人很快就能上完厕所,我只需要再等一会儿就行了,没有必要重新找,重新找就太麻烦了,明显成本更高
所以我们等待的这个操作就叫做自旋
才计算机中也是同样的道理,某个线程在执行程序的时候,我们判断八成很快就能执行完,所以我们原地自旋,这就是自旋。如果反复自旋,里面的线程始终不释放锁,我们可以进行锁粗化。