CAS操作
一、什么是CAS操作?
-
首先来说一下悲观锁策略
线程在获取锁的时候,采取的是一种悲观锁的策略。即认为每执行一次临界区代码都会产生冲突,所以当前线程获取锁的时候也会阻塞其他线程获取锁。
-
CAS操作
CAS操作又称无锁操作,采取的是一种***乐观锁***的策略,即***它假设所有线程资源在访问共享资源时不会出现冲突***,所以不会阻塞其他线程的操作。因此线程不会出现阻塞停顿的状态。
那么如果真的出现了冲突怎么办?无锁操作是使用***CAS(Compare And Swap)***,即字面意思,比较和交换,以此来鉴别线程是否出现冲突,出现了冲突就重试当前操作直到没有冲突为止。
二、CAS操作过程
- 首先我们可以假想有这么一个方法 CAS(V, O, N),其三个参数分别意为:V内存地址存放的实际值,O预期的值(旧的值),N更新的新值。
- 当O和V相同时,即旧值和内存中实际的值相同,表明该值没有被其他线程修改过,旧值O就是目前来说最新的值了,自然就可以将新值N赋给V了。
- 当O和V不同时,表明该值被其他线程修改过了,旧值O不是最新版本的值,所以不能将N赋给V,返回V即可。
- 另外,当 多个线程使用CAS操作一个变量时,只有一个线程会成功,并成功更新,其余则会失败。当然,失败的线程会重新尝试,也可以选择挂起。
TIPS:CAS操作的实现需要硬件指令集的支持,在JDK1.5以后的虚拟机才可以实现。而且CAS并不会将线程就那么简单的挂起,CAS失败以后会进行一定的尝试,而非进行进行耗时的挂起或者唤醒操作,也称为非阻塞同步。
三、CAS的问题
- 用一个例子来讲,就是一个是***从A变成B,又从B变成A***,CAS检查时发现旧值依然与新值相等,但是实际上发生了变化。这里我们的解决方案就是类似版本管理工具之类的作法,添加一个版本号。
- 自旋会浪费处理器资源,(自旋: 如同前面说的CAS失败不会立即挂起,而是继续争夺一段时间,而后才放弃),这里我们采取的策略是,如果在一定时间内获取到了资源,那么下次延长这个时间,如果没有获取到,那么下次减短这个时间。
- 不公平,处于阻塞状态的线程无法获取刚释放的资源,而处于自旋状态的线程很有可能会优先获得这把锁。
TIPS: 内建锁无法实现公平机制,而Lock体系可以实现公平锁。