无锁机制----比较交换CAS Compare And Swap
一、锁与共享变量
加锁是一种悲观的策略,它总是认为每次访问共享资源的时候,总会发生冲突,所以宁愿牺牲性能(时间)来保证数据安全。
无锁是一种乐观的策略,它假设线程访问共享资源不会发生冲突,所以不需要加锁,因此线程将不断执行,不需要停止。一旦碰到冲突,就重试当前操作直到没有冲突为止。
无锁的策略使用一种叫做比较交换的技术(CAS Compare And Swap)来鉴别线程冲突,一旦检测到冲突产生,就重试当前操作直到没有冲突为止。
二、无锁如何鉴别冲突
CAS核心算法:执行函数:CAS(V,E,N)
V表示准备要被更新的变量
E表示我们提供的 期望的值
N表示新值 ,准备更新V的值
算法思路:V是共享变量,我们拿着自己准备的这个E,去跟V去比较,如果E == V ,说明当前没有其它线程在操作,所以,我们把N 这个值 写入对象的 V 变量中。如果 E != V ,说明我们准备的这个E,已经过时了,所以我们要重新准备一个最新的E ,去跟V 比较,比较成功后才能更新 V的值为N。
三、无锁的效果
如果多个线程同时使用CAS操作一个变量的时候,只有一个线程能够修改成功。其余的线程提供的期望值已经与共享变量的值不一样了,所以均会失败。
由于CAS操作属于乐观派,它总是认为自己能够操作成功,所以操作失败的线程将会再次发起操作,而不是被OS挂起。所以说,即使CAS操作没有使用同步锁,其它线程也能够知道对共享变量的影响。
因为其它线程没有被挂起,并且将会再次发起修改尝试,所以无锁操作即CAS操作天生免疫死锁。
另外一点需要知道的是,CAS是系统原语,CAS操作是一条CPU的原子指令,所以不会有线程安全问题。
四、Java提供的CAS操作:原子操作类
Java提供了一个Unsafe类,其内部方法操作可以像C的指针一样直接操作内存,方法都是native的。
为了让Java程序员能够受益于CAS等CPU指令,JDK并发包中有一个atomic包,它们是原子操作类,它们使用的是无锁的CAS操作,并且统统线程安全。atomic包下的几乎所有的类都使用了这个Unsafe类。
分类如下:
这些类中,最有代表性的就是AtomicInteger类。
看源码,省略了部分代码
-
public class AtomicInteger extends Number implements java.io.Serializable {
-
private static final long serialVersionUID = 6214790243416807050L;
-
-
// 这个就是封装CAS操作的指针
-
private static final Unsafe unsafe = Unsafe.getUnsafe();
-
-
//原来内部的共享变量,就是这个value,并且使用volatile让其在多个线程之间可见
-
private volatile int value;
-
-
//初始化的构造函数
-
public AtomicInteger(int initialValue) {
-
value = initialValue;
-
}
-
-
//获取当前值
-
public final int get() {
-
return value;
-
}
-
-
//设置当前的共享变量的值
-
public final void set(int newValue) {
-
value = newValue;
-
}
-
-
//使用CAS操作设置新的值,并且返回旧的值
-
public final int getAndSet(int newValue) {
-
//使用指针unsafe类的三大原子操作方法之一
-
return unsafe.getAndSetInt(this, valueOffset, newValue);
-
}
-
-
-
//把expect与内部的value进行比较,如果相等,那么把value的值设置为update的值
-
public final boolean compareAndSet(int expect, int update) {
-
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
-
}
-
-
//返回value,并把value + 1
-
public final int getAndIncrement() {
-
return unsafe.getAndAddInt(this, valueOffset, 1);
-
}
-
-
//自增,并且返回自增后的值
-
public final int incrementAndGet() {
-
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
-
}
-
-
}
查看指针类unsafe类的incrementAndGet方法的代码实现,颇具教学意义。
这个方法是一个死循环,不断尝试获取最新的值,也就不断获取 CAS(V,E,N)中的E,也就是我们要提供的期望的值。
如果此时 共享变量V 与 我们的 E 相同,那么就把 V 的值 修改成 N。
下面代码中,先不断尝试获取最新的共享变量的值V,如果其它线程也在同时获取V,并且其它线程抢先将共享变量V 修改成了 V+1,那么此时,当前线程持有的共享变量的值是V,它去与实际的共享变量值V+1比较,将会比较失败,所以本次自增失败。但是因为是一个死循环,当前线程将会重新调用 get()方法获取最新的值,直到在其它线程执行CAS操作之前,抢先执行自增共享变量的操作。
-
public final int incrementAndGet(){
-
for(;;){
-
int current = get();
-
int next = current + 1;
-
if(compareAndSet(current,next)){
-
return next;
-
}
-
}
-
}
五、ABA问题及其解决方案
在CAS的核心算法中,通过死循环不断获取最新的E。如果在此之间,V被修改了两次,但是最终值还是修改成了旧值V,这个时候,就不好判断这个共享变量是否已经被修改过。为了防止这种不当写入导致的不确定问题,原子操作类提供了一个带有时间戳的原子操作类。
带有时间戳的原子操作类AtomicStampedReference (音:a tommy k S dan P de ..)
CAS(V,E,N)
当带有时间戳的原子操作类AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。
当AtomicStampedReference设置对象值时,对象值以及时间戳都必须满足期望值,写入才会成功。因此,即使对象值被反复读写,写回原值,只要时间戳发生变化,就能防止不恰当的写入。
底层实现为: 通过Pair私有内部类存储数据和时间戳, 并构造volatile修饰的私有实例
接着看AtomicStampedReference类的compareAndSet()方法的实现:
同时对当前数据和当前时间进行比较,只有两者都相等是才会执行casPair()方法,
单从该方法的名称就可知是一个CAS方法,最终调用的还是Unsafe类中的compareAndSwapObject方法
到这我们就很清晰AtomicStampedReference的内部实现思想了,
通过一个键值对Pair存储数据和时间戳,在更新时对数据和时间戳进行比较,
只有两者都符合预期才会调用Unsafe的compareAndSwapObject方法执行数值和时间戳替换,也就避免了ABA的问题。
-
public class AtomicStampedReference<V> {
-
//通过一个volatile修饰的Pair对象
-
private volatile Pair<V> pair;
-
-
//嵌套类Pair技能存储对象引用,也存储了时间戳
-
private static class Pair<T> {
-
final T reference;
-
final int stamp;
-
private Pair(T reference, int stamp) {
-
this.reference = reference;
-
this.stamp = stamp;
-
}
-
-
public boolean compareAndSet(V expectedReference,
-
V newReference,
-
int expectedStamp,
-
int newStamp) {
-
Pair<V> current = pair;
-
return
-
expectedReference == current.reference &&
-
expectedStamp == current.stamp &&
-
((newReference == current.reference &&
-
newStamp == current.stamp) ||
-
casPair(current, Pair.of(newReference, newStamp)));
-
}
-
-
-
-
}
原文链接:https://blog.csdn.net/yanluandai1985/article/details/82686486
无锁机制----比较交换CAS Compare And Swap的更多相关文章
- 非阻塞同步算法与CAS(Compare and Swap)无锁算法
锁(lock)的代价 锁是用来做并发最简单的方式,当然其代价也是最高的.内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放. ...
- 【Java并发编程】9、非阻塞同步算法与CAS(Compare and Swap)无锁算法
转自:http://www.cnblogs.com/Mainz/p/3546347.html?utm_source=tuicool&utm_medium=referral 锁(lock)的代价 ...
- CAS无锁机制原理
原子类 java.util.concurrent.atomic包:原子类的小工具包,支持在单个变量上解除锁的线程安全编程 原子变量类相当于一种泛化的 volatile 变量,能够支持原子的和有条件的读 ...
- CAS(Compare and Swap)无锁算法-学习笔记
非阻塞同步算法与CAS(Compare and Swap)无锁算法 这篇问题对java的CAS讲的非常透彻! 锁的代价 1. 内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的 ...
- Atitit。Cas机制 软件开发 编程语言 无锁机制 java c# php
Atitit.Cas机制 软件开发 编程语言 无锁机制 java c# php 1. 为什么需要无锁操作1 2. 硬件支持 cas atomic2 3. 无锁编程(Lock-Free)就是在某些应用 ...
- 二、多线程基础-乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁
1.10乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将 比较-设置 ...
- 无锁的同步策略——CAS操作详解
目录 1. 从乐观锁和悲观锁谈起 2. CAS详解 2.1 CAS指令 2.3 Java中的CAS指令 2.4 CAS结合失败重试机制进行并发控制 3. CAS操作的优势和劣势 3.1 CAS相比独占 ...
- CAS(Compare and Swap)理解
什么叫CAS(Compare and Swap)? 硬件同步原语!! 什么蛋疼的名字,一般人很难理解.根据英文全称翻译==比较与交换,这个名字大致还能理解一点,目前先暂且这么理解吧. 有啥用处? 对 ...
- 浅谈CAS(Compare and Swap) 原理
浅谈CAS原理java并发编程也研究了一段时间了,对CAS的原理总是不太理解,今天再研究了一下,记录一些自己的理解. 说到CAS,再java中的某些情况下,甚至jdk1.5以后的大多数情况,并发 ...
随机推荐
- el-table 操作列(编辑or删除) 获取本行相关数据
简单说明:开发的时候,经常会遇到表格后面跟着操作列,一般都是编辑或者删除,那么 就需要获取到 本行数据相关的id或者其他附属信息.ok,下边放代码 //vue el-table的部分代码 <el ...
- Android复习准备
1. 四大组件是什么? Activity(活动):用于表现功能 Service(服务):后台运行服务,不提供界面呈现 BroadcastReceiver(广播接收器):用来接收广播 ContentPr ...
- PHP array_reduce() 函数
实例 发送数组中的值到用户自定义函数,并返回一个字符串: <?phpfunction myfunction($v1,$v2){return $v1 . "-" . $v2;} ...
- PHP curl_strerror函数
(PHP 5 >= 5.5.0) curl_strerror — 返回错误码的描述. 说明 string curl_strerror ( int $errornum ) 返回错误码的文本描述信息 ...
- ZROI 提高十连测 DAY3
由于我不太会写 觉得从比赛开始就冷静分析.然后看完三道题心态有点爆炸没有紧扣题目的性质. 这个心态是不可取的尽量不要有畏难心理 不要草草的写暴力. LINK:[最长01子序列](http://zhen ...
- 解决痛苦的方法/cy
这篇文章 源于我所有痛苦的回忆. 由于痛苦太多了 体会完了 所以 觉得这些都不算是什么大问题了 所以 这里 是解决痛苦的方法. 真的很痛苦的话 可以这样 对着全班人喊你们 都没我强 因为 你们都没有我 ...
- 搭建Redis主从复制的集群
在主从复制模式的集群里,主节点一般是一个,从节点一般是两个或多个,写入主节点的数据会被复制到从节点上,这样一旦主节点出现故障,应用系统能切换到从节点去读写数据,这样能提升系统的可用性.而且如果再采用主 ...
- 安装ElasticSearch遇到的深坑
实验需要ES,安装过程中遇到一些奇葩的问题,记录下.下面介绍下安装步骤: 第一步:安装java ES是运行在java虚拟机上面的,所以首先需要安装java环境,安装过程不再赘述,唯一需要注意的是ES对 ...
- Qt自定义控件之仪表盘2--QPaint绘制仪表盘
0.前言 前面一篇文章写道了仪表盘的特点,实现了一个贴图的仪表盘,属于低配版本的仪表盘. 主要是有任何改动时候就需要重新设计图片,不能适配不同控件大小,即使让它自由拉伸,但仪表盘放大缩小时候显示 ...
- 安装Hive 使用beeline 链接 出现 User: AAA is not allowed to impersonate BBB
AAA 指的是 hdfs 文件系统的用户 BBB 是hive 设置的 hiveserver2 配置文件中的登陆用户名 在hadoop 配置如下 <property> <name> ...