CAS(乐观锁)以及ABA问题
https://blog.csdn.net/wwd0501/article/details/88663621
独占锁是一种悲观锁,synchronized就是一种独占锁;它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起直到持有锁的线程释放锁。
所谓乐观锁就是每次不加锁,假设没有冲突而去完成某项操作;如果发生冲突了那就去重试,直到成功为止。
CAS(Compare And Swap)是一种有名的无锁算法。CAS算法是乐观锁的一种实现。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B并返回true,否则返回false。
注:synchronized和ReentrantLock都是悲观锁。
注:什么时候使用悲观锁效率更高、什么使用使用乐观锁效率更高,要根据实际情况来判断选择。
什么是CAS机制
CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
这样说或许有些抽象,我们来看一个例子:
1.在内存地址V当中,存储着值为10的变量。
2.此时线程1想要把变量的值增加1。对线程1来说,旧的预期值A=10,要修改的新值B=11。
3.在线程1要提交更新之前,另一个线程2抢先一步,把内存地址V中的变量值率先更新成了11。
4.线程1开始提交更新,首先进行A和地址V的实际值比较(Compare),发现A不等于V的实际值,提交失败。
5.线程1重新获取内存地址V的当前值,并重新计算想要修改的新值。此时对线程1来说,A=11,B=12。这个重新尝试的过程被称为自旋。
6.这一次比较幸运,没有其他线程改变地址V的值。线程1进行Compare,发现A和地址V的实际值是相等的。
7.线程1进行SWAP,把地址V的值替换为B,也就是12。
从思想上来说,Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。
CAS的优缺点:
乐观锁避免了悲观锁独占对象的现象,同时也提高了并发性能,乐观锁是对悲观锁的改进,虽然它也有缺点,但它确实已经成为提高并发性能的主要手段,而且jdk中的并发包也大量使用基于CAS的乐观锁。但它也有缺点,如下:
1.CPU可能开销较大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
2.不能保证代码块的原子性
CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用悲观锁了。
3.ABA问题。
CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。
ABA问题:
线程1准备用CAS修改变量值A,在此之前,其它线程将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了。
ABA问题处理:
思路:解决ABA最简单的方案就是给值加一个修改版本号,每次值变化,都会修改它版本号,CAS操作时都对比此版本号。
JAVA中ABA中解决方案(AtomicStampedReference/AtomicMarkableReference)
AtomicStampedReference 本质是有一个int 值作为版本号,每次更改前先取到这个int值的版本号,等到修改的时候,比较当前版本号与当前线程持有的版本号是否一致,如果一致,则进行修改,并将版本号+1(当然加多少或减多少都是可以自己定义的),在zookeeper中保持数据的一致性也是用的这种方式;
AtomicMarkableReference则是将一个boolean值作是否有更改的标记,本质就是它的版本号只有两个,true和false,修改的时候在这两个版本号之间来回切换,这样做并不能解决ABA的问题,只是会降低ABA问题发生的几率而已;
例子:
private static AtomicStampedReference<Integer> atomicStampedRef =
new AtomicStampedReference<>(1, 0);
public static void main(String[] args){
Thread main = new Thread(() -> {
System.out.println("操作线程" + Thread.currentThread() +",初始值 a = " + atomicStampedRef.getReference());
int stamp = atomicStampedRef.getStamp(); //获取当前标识别
try {
Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1); //此时expectedReference未发生改变,但是stamp已经被修改了,所以CAS失败
System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);
},"主操作线程"); Thread other = new Thread(() -> {
Thread.yield(); // 确保thread-main 优先执行
atomicStampedRef.compareAndSet(1,2,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ atomicStampedRef.getReference());
atomicStampedRef.compareAndSet(2,1,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ atomicStampedRef.getReference());
},"干扰线程"); main.start();
other.start();
}
文章参考:https://www.jianshu.com/p/ae25eb3cfb5d
https://blog.csdn.net/justry_deng/article/details/83449038
CAS(乐观锁)以及ABA问题的更多相关文章
- CAS(乐观锁)与ABA问题
cas是什么 CAS 全称 compare and swap 或者compare and exchange 比较并且交换.用于在没有锁的情况下,多个线程对同一个值的更新. cas原理 例如,我们对一 ...
- Java性能 -- CAS乐观锁
synchronized / Lock / CAS synchronized和Lock实现的同步锁机制,都属于悲观锁,而CAS属于乐观锁 悲观锁在高并发的场景下,激烈的锁竞争会造成线程阻塞,而大量阻塞 ...
- [数据库锁机制] 深入理解乐观锁、悲观锁以及CAS乐观锁的实现机制原理分析
前言: 在并发访问情况下,可能会出现脏读.不可重复读和幻读等读现象,为了应对这些问题,主流数据库都提供了锁机制,并引入了事务隔离级别的概念.数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务 ...
- 并发之ATOMIC原子操作--CAS乐观锁原理(二)
1.乐观锁介绍 程序完成并发操作时,访问数据时每次不加锁,假设没有冲突去完成某项操作,如果因为冲突失败就重试,直到成功为止.就是当去做某个修改或其他操作的时候它认为不会有其他线程来做同样的操作(竞争) ...
- redis的高级事务CAS(乐观锁)
Optimistic locking using check-and-set(乐观锁) 乐观锁介绍:watch指令在redis事物中提供了CAS的行为.为了检测被watch的keys在是否有多个cli ...
- Java:CAS(乐观锁)
本文讲解CAS机制,主要是因为最近准备面试题,发现这个问题在面试中出现的频率非常的高,因此把自己学习过程中的一些理解记录下来,希望能对大家也有帮助. 什么是悲观锁.乐观锁?在java语言里,总有一些名 ...
- JAVA多线程学习四 - CAS(乐观锁)
本文讲解CAS机制,主要是因为最近准备面试题,发现这个问题在面试中出现的频率非常的高,因此把自己学习过程中的一些理解记录下来,希望能对大家也有帮助. 什么是悲观锁.乐观锁?在java语言里,总有一些名 ...
- java 乐观锁CAS
乐观锁是一种思想,本身代码里并没有lock或synchronized关键字进行修饰.而是采用一种version. 即先从数据库中查询一条记录得到version值,在更新这条记录时在where条件中对这 ...
- Java并发:乐观锁
作者:汤圆 个人博客:javalover.cc 简介 悲观锁和乐观锁都属于比较抽象的概念: 我们可以用拟人的手法来想象一下: 悲观锁:像有些人,凡事都往坏的想,做最坏的打算:在java中就表现为,总是 ...
随机推荐
- 【DevCloud · 敏捷智库】两种你必须了解的常见敏捷估算方法
背景 在某开发团队辅导的回顾会议上,团队成员对于优化估计具体方法上达成了一致意见.询问是否有什么具体的估计方法来做估算. 问题分析 回顾意见上大家对本次Sprint的效果做回顾,其中80%的成员对于本 ...
- 题解:2018级算法第五次上机 C5-图2
题目描述: 样例: 实现解释: 所有结点对最短路径的板子题 知识点: 寻找所有结点对最短路径,动态规划 坑点: 无坑,注意建边即可 使用的算法为floyd算法 按照程序顺序解释如下: 首先建图,以邻接 ...
- Mysql基础(三):MySQL基础数据类型、完整性约束、sql_mode模式
目录 2.MySQL基础数据类型.完整性约束.sql_mode模式 1. MySQL常用数据类型 2. 完整性约束 3. MySQL的sql_mode模式说明以及设置 2.MySQL基础数据类型.完整 ...
- Django框架12 /同源、跨域、CORS
Django框架12 /同源.跨域.CORS 目录 Django框架12 /同源.跨域.CORS 1. 同源策略 2. 跨域 3. 解决跨域:CORS/跨域资源共享 1. 同源策略 同源策略(Same ...
- linux专题(三):常用的基本命令(一)目录管理
http://dwz.date/UDf 绝对路径和相对路径 我们知道Linux的目录结构为树状结构,最顶级的目录为根目录 /. 其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们. 在开始 ...
- bzoj3062[Usaco2013 Feb]Taxi*
bzoj3062[Usaco2013 Feb]Taxi 题意: Bessie在农场上为其他奶牛提供出租车服务,她必须赶到这些奶牛的起始位置,并把他们带到它们的目的地.Bessie的车很小,所以她只能一 ...
- 2.5万字长文简单总结SpringMVC请求参数接收
这是公众号<Throwable文摘>发布的第22篇原创文章,暂时收录于专辑<架构与实战>.暂定下一篇发布的长文是<图文分析JUC同步器框架>,下一篇发布的短文是&l ...
- 使用nvm安装node,运行node报错 node: command not found
1. 使用nvm安装node之后,直接运行node命令会报错 node: command not found 需要使用nvm ls 查询一下当前使用的安装的node版本,然后使用node use 版 ...
- C++ RMQ问题
RMQ问题是区间求最值问题,就是求一个数组第i个到第j个中最大数或最小数的算法. 这个算法有一些倍增思想,也有一些二分思想.具体是一个数组,m[i][j]表示从i开始往后数2的j次方个数的最大值或最小 ...
- 【Nginx】面试官问我Nginx能不能配置WebSocket?我给他现场演示了一番!!
写在前面 当今互联网领域,不管是APP还是H5,不管是微信端还是小程序,只要是一款像样点的产品,为了增加用户的交互感和用户粘度,多多少少都会涉及到聊天功能.而对于Web端与H5来说,实现聊天最简单的就 ...