并发策略-CAS算法
对于并发控制而言,我们平时用的锁(synchronized,Lock)是一种悲观的策略。它总是假设每一次临界区操作会产生冲突,因此,必须对每次操作都小心翼翼。如果多个线程同时访问临界区资源,就宁可牺牲性能让线程进行等待,所以锁会阻塞线程执行。
与之相对的有一种乐观的策略,它会假设对资源的访问是没有冲突的。既然没有冲突也就无需等待了,所有的线程都在不停顿的状态下持续执行。那如果遇到问题了无锁的策略使用一种叫做比较交换(CAS Compare And Swap)来鉴别线程冲突,一旦检测到冲突产生,就重试当前操作直到没有冲突。CAS算法是非阻塞的,它对死锁问题天生免疫,而且它比基于锁的方式拥有更优越的性能。
CAS算法的过程是这样:它包含三个参数 CAS(V,E,N)。V表示要更新的变量,E表示预期的值,N表示新值。仅当V值等于E值时,才会将V的值设置成N,否则什么都不做。最后CAS返回当前V的值。CAS算法需要你额外给出一个期望值,也就是你认为现在变量应该是什么样子,如果变量不是你想象的那样,那说明已经被别人修改过。你就重新读取,再次尝试修改即可。
JDK并发包有一个atomic包,里面实现了一些直接使用CAS操作的线程安全的类型。其中最常用的一个类应该就是AtomicInteger。我们以此为例来研究一下没有锁的情况下如何做到线程安全。
private volatile int value;
这是AtomicInteger类的核心字段,代表当前实际取值,借助volatile保证线程间数据的可见性。
获取内部数据的方法:
public final int get() { return value; }
我们关注一下incrementAndGet()的内部实现
public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }
代码第二行使用了一个死循环,原因是:CAS的操作未必都是成功的,因此对于不成功的情况,我们就需要进行不断的尝试。第三行取得当前值,接着+1得到新值next。这里我们使用CAS必需的两个参数:期望值以及新值。使用compareAndSet()将新值next写入。成功的条件是在写入的时刻,当前的值应该要等于刚刚取到的current。如果不是这样则说明AtomicInteger的值在第3行到第5行之间被其他线程修改过了。当前看到的状态是一个过期的状态,因此返回失败,需要进行下一次重试,知道成功为止。
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
整体的过程就是这样子的,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。大概的逻辑应该是这样:
if (this == expect) { this = update return true; } else { return false; }
CAS虽然能高效的解决原子问题,但是CAS也会带来1个经典问题即ABA问题:
因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。
从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类在内部不仅维护了对象值,还维护了一个时间戳(可以是任意的一个整数来表示状态值)。当设置对象值时,对象值和状态值都必须满足期望值才会写入成功。因此即使对象被反复读写,写会原值,只要状态值发生变化,就能防止不恰当的写入。
/** * @param expectedReference 期望值 * @param newReference 写入新值 * @param expectedStamp 期望状态值 * @param newStamp 新状态值 * @return true if successful */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))); }
并发策略-CAS算法的更多相关文章
- 聊聊并发(六)——CAS算法
一.原子类 1.CAS算法 强烈建议读者看这篇之前,先看这篇 初识JUC 的前两节,对原子性,原子变量,内存可见性有一个初步认识. CAS(Compare and Swap)是一种硬件对并发的支持,针 ...
- (转载)java高并发:CAS无锁原理及广泛应用
java高并发:CAS无锁原理及广泛应用 版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...
- 详解 volatile关键字 与 CAS算法
(请观看本人博文 -- <详解 多线程>) 目录 内存可见性问题 volatile关键字 CAS算法: 扩展 -- 乐观锁 与 悲观锁: 悲观锁: 乐观锁: 在讲解本篇博文的知识点之前,本 ...
- volatile关键字与内存可见性&原子变量与CAS算法
1 .volatile 关键字:当多个线程进行操作共享数据时, 可以保证内存中的数据可见 2 .原子变量:jdk1.5后java.util.concurrent.atomic 包下提供常用的原子变量 ...
- 三、原子变量与CAS算法
原子变量:jdk1.5 后 java.util.concurrent.atomic 包下提供了常用的原子变量: - AtomicBoolean - AtomicInteger - AtomicLong ...
- Java多线程系列——原子类的实现(CAS算法)
1.什么是CAS? CAS:Compare and Swap,即比较再交换. jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronou ...
- (一)juc线程高级特性——volatile / CAS算法 / ConcurrentHashMap
1. volatile 关键字与内存可见性 原文地址: https://www.cnblogs.com/zjfjava/category/979088.html 内存可见性(Memory Visibi ...
- 原子性 CAS算法
一. i++ 的原子性问题 1.问题的引入: i++ 的实际操作分为三个步骤:读--改--写 实现线程,代码如下: public class AtomicDemo implements Runnabl ...
- java CAS算法
CAS算法是硬件对于并发操作的支持,其中包含了三个操作数:内存值,预估值和更新值.没当要执行更新操作时,会先在同步方法中比较内存值和预估值是否相等,如果相等才会用更新值替换内存值,否则什么也不做. p ...
随机推荐
- 19,Ubuntu安装之python开发
什么??公司要用Ubuntu(乌班图)?不会用??怎么进行python开发??? 乌班图操作系统下载地址:http://releases.ubuntu.com/18.04/ubuntu-18.04 ...
- P2680 运输计划(二分+树上差分)
P2680 运输计划 链接 分析: 二分+树上差分. 首先可以二分一个答案,那么所有比这个答案大的路径,都需要减去些东西才可以满足这个答案. 那么减去的这条边一定在所有的路径的交集上. 那么如果求快速 ...
- CSAcademy Palindromic Concatenation 字符串哈希
题意: 题目链接 给出\(n\)个字符串,求有多少对\((i,j),i \neq j\)使得\(s_i\)与\(s_j\)拼起来是回文串 分析: 设\(s_i,s_j\)的长度分别为\(L_i, L_ ...
- Hyper-V在线调整虚拟硬盘大小
从Windows Server 2012 R2 开始,可以在线调整虚拟硬盘的大小了,这意味着当虚拟硬盘不够用时,我们在虚拟机运行的情况下直接扩展虚拟硬盘容量了.有人说这个有什么用?当然,实验室情况下, ...
- 《Cracking the Coding Interview》——第11章:排序和搜索——题目7
2014-03-21 22:05 题目:给你N个盒子堆成一座塔,要求下面盒子的长和宽都要严格大于上面的.问最多能堆多少个盒子? 解法1:O(n^2)的动态规划解决.其实是最长递增子序列问题,所以也可以 ...
- 二分法求函数值的Pascal实现
用二分法求在(a,b)上单调的函数近似值 第八行的表达式可更改,第三行的kexi决定的精度,小数值计算可将第五行的extended更为real或double PROGRAM EQUANTION ( ...
- Robot Framwork +Selenium2环境搭建
Robot Framwork +Selenium2环境搭建 安装python 参考文章http://blog.csdn.net/sealion111/article/details/78690686 ...
- Restful API实战
简介:随着移动互联网的发展,客户端层出不穷,app,web,微信端等等,而后端业务逻辑基于是一致的,如何做到业务逻辑“一次编写,随时接入”?答案是通过远程调用API,而目前比较火的方案是“Restfu ...
- KMS激活windows
slmgr /skms 106.0.6.209 slmgr /ato .查看当前系统是否永久激活,命令的作用是查看当前许可证状态的截止日期 slmgr.vbs -xpr 查看Windows正式版产品密 ...
- Elasticsearch同义词词汇单元过滤器
1 简单扩展 "jump,hop,leap" 搜索jump会检索出包含jump.hop或leap的词 1.1 扩展应用在索引阶段 1.2 扩展应用在查询阶段 1.3 对比 2 简单 ...