乐观锁实现之CAS算法分析
在介绍 CAS 之前,先来了解下什么是乐观锁。
乐观锁(Optimistic Lock)是指对于数据冲突保持一种乐观态度,操作数据时不会对数据加锁,只有到数据提交的时候才通过某种机制来验证数据是否存在冲突。
可以通过使用版本号和 CAS 算法进行实现,本篇博客主要介绍 CAS 算法的概念,以及对 CAS 算法的实现原理进行分析。
什么是 CAS 算法
CAS:Compare and Swap,即比较再交换,其算法公式如下:
函数公式:CAS(V,E,N)
CAS 操作需要我们提供一个期望值,当期望值与当前线程的变量值相同时,说明还没有线程修改该值,当前线程就可以进行修改,也就是执行 CAS 操作。
但如果期望值与当前线程的变量值不符,则说明该值已被其他线程修改,此时不执行更新操作,但可以选择重新读取该变量并尝试再次修改,也可以放弃操作。
CAS 的实现原理
对 java.util.concurrent.atomic 包下的原子类 AtomicInteger 中的 compareAndSet 方法进行分析,可以发现最终调用的是 sum.misc.Unsafe 这个类。
/*** Atomically sets the value to the given updated value* if the current value {@code ==} the expected value.** @param expect the expected value* @param update the new value* @return {@code true} if successful. False return indicates that* the actual value was not equal to the expected value.*/public final boolean compareAndSet(long expect, long update) {return unsafe.compareAndSwapLong(this, valueOffset, expect, update);}
Unsafe 类本身是不安全的,它为了速度,在 Java 的安全标准上做出了一定的妥协。Unsafe 的 CAS 依赖的是 JVM 针对不同的操作系统实现的 Atomic::cmpxchg 函数。
Atomic::cmpxchg 函数使用了汇编的 CAS 操作,并使用 CPU 硬件提供的 lock 信号保证其原子性的实现。
CAS 的优缺点
优点
CAS 是非阻塞的轻量级乐观锁,通过 CPU 指令实现。在资源竞争不激烈的情况下,synchronized 重量锁会进行比较复杂的加锁、解锁和唤醒操作,而 CAS 不会加锁,性能高。
缺点
CPU开销大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量却又一直更新不成功,会给 CPU带来很大压力。
不能保证代码块的原子性
CAS 机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用 synchronized 同步锁了。
除了以上两个缺点,CAS 还可能会出现 ABA 的问题,那么 ABA 问题又是什么呢?
CAS 中的 ABA 问题
如果一开始位置 V 得到的旧值是 A ,当进行赋值操作时再次读取发现仍然是 A ,并不能说明变量没有被其它线程改变过,有可能是其它线程将变量改为了 B,后来又改回了 A。
对于 ABA 问题,主要有两种解决方案:
使用版本号
在变量前面追加版本号,每次变量更新的时候把版本号加一,也就是说,之前的 A-B-A 就会变成 1A - 2B - 3A。
使用并发包的原子类
java.util.concurrent.atomic 包下提供了一个可处理 ABA 问题的原子类 AtomicStampedReference。其 compareAndSet 方法首先会检查当前引用是否等于预期引用,且当前标志是否等于预期标志。
如果全部相等,则以原子方式将该引用的该标志的值设置为给定的更新值。
CAS 的使用场景
Atomic 开头的实现类
以 Atomic 开头的实现类的底层都使用了 CAS 算法。
自旋锁
自旋锁是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程不会阻塞,而是将循环等待,不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。可以看成是一个不断自动重试的乐观锁,它是 CAS 算法的一种锁机制的实现。
- 令牌桶限流器
系统以恒定的速度向桶内增加令牌,每次请求前从令牌桶里面获取令牌,只有获取到令牌就才可以进行访问。
当令牌桶内没有令牌的时候,就会拒绝提供服务。其中的限流器就是通过使用 CAS 算法来维护多线程环境下对令牌的增加和分发的。
参考了如下博客,非常感谢:
Java并发编程之CAS算法
java中的cas - 知乎
乐观锁实现之CAS算法分析相关推荐
- 乐观锁,CAS,ABA问题
文章目录 常见的锁策略 乐观锁 vs 悲观锁 读写锁 自旋锁(Spin Lock) 可重入锁 什么是 CAS CAS 是怎么实现的 ABA 问题(乐观锁导致的) synchronized 锁 常见的锁 ...
- 【java】乐观锁和悲观锁、CAS和ABA问题
一.乐观锁VS悲观锁 1)关于悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,Java中synchronized和ReentrantLock以及Read ...
- 面试官问:说说悲观锁、乐观锁、分布式锁?都在什么场景下使用?有什么技巧?...
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 如何确保一个方法,或者一块代码在高并发情况下,同一时间只能 ...
- 说说悲观锁、乐观锁、分布式锁
作者 | 张飞洪 来源 | https://www.cnblogs.com/jackyfei/p/12142840.html 如何确保一个方法,或者一块代码在高并发情况下,同一时间只能被一个线程执行, ...
- 乐观锁与悲观锁及其实现
乐观锁与悲观锁及其实现 乐观锁 每次操作时不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止 悲观锁 是会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁. 乐观锁可 ...
- 【mysql】悲观锁和乐观锁的实现原理
java多线程中的锁分类多种多样,其中有一种主要的分类方式就是乐观和悲观进行划分的. 一.乐观锁概念 说是写乐观锁的概念,但是通常乐观锁和悲观锁的概念都要一块写.对比着来才更有意义. 1.悲观锁概念 ...
- zbb20180929 thread 自旋锁、阻塞锁、可重入锁、悲观锁、乐观锁、读写锁、对象锁和类锁...
1.自旋锁 自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行.若线程依然不能获得锁,才会被挂 ...
- Java之原子性-乐观锁与悲观锁
1.volatile-问题 1.1.代码分析 : package com.itheima.myvolatile;public class Demo {public static void main(S ...
- 乐观锁 VS 悲观锁
乐观锁 VS 悲观锁 悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁.写锁.行锁等),当其他线程想要访问数据时,都需要阻塞挂起. 乐观锁:总是认为不会产生并发问题,每 ...
- Java悲观锁与乐观锁
Java悲观锁与乐观锁 锁的目的 实例 悲观锁实现 乐观锁实现 总结 锁的目的 多线程编程如有共用资源的使用时,需要保证数据安全,资源需要同步处理.处理资源的手段可以有:互斥同步与非阻塞同步.实现分别 ...
最新文章
- mysql数据库开发规范_开发规范——MYSQL数据库
- linux中bash的功能主要有,Linux系统中的Bash功能的介绍
- java真的是值传递么?
- (转载)网络编程释疑之:同步,异步,阻塞,非阻塞
- java excutorthread_Java中ThreadPoolExecutor的参数理解
- 自动论文生成器 python_python生成器
- 2021-06-16异步调用 CompletableFuture
- Boblog热门日志、随机日志、热门Tags插件源代码
- 商业计划书(BP)应该包含哪些点?看 BP 的人最想从中得到什么?
- 华为当个pl怎么样_华为8PL∪S提示灯 | 手游网游页游攻略大全
- python背景颜色代码大全_Python实现转换图片背景颜色代码
- GooglTest GoogleMock 实践感想三 死亡测试初步(1)
- r语言实现岭回归_R语言回归篇
- 网页形式的php抓取文件,PHP 抓取网页源文件
- Kubernetes切换Docker容器引擎为Containerd
- vue cli3 配置sass全局变量设置不生效,sass混合器文件全局引入
- html五角星代码,五角星评分系统.html
- Hadoop:MapReduce编程之统计每个订单价格最高的商品信息
- 这种动态条形图+折线图怎么做?今天我来教你!
- UVALive 4513 Stammering Aliens