对于并发控制而言,我们平时用的锁(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)));}

> 个人公众号:JAVA日知录 , javadaily.cn

并发策略-CAS算法相关推荐

  1. java cas原理_Java并发之原子变量及CAS算法-上篇

    Java并发之原子变量及CAS算法-上篇 编辑 ​ 概述 本文主要讲在Java并发编程的时候,如果保证变量的原子性,在JDK提供的类中是怎么保证变量原子性的呢?.对应Java中的包是:java.uti ...

  2. 线程执行完之后会释放吗_java多线程并发:CAS+AQS+HashMap+volatile+ThreadLocal,乐分享...

    CyclicBarrier.CountDownLatch.Semaphore 的用法 CountDownLatch(线程计数器 ) CountDownLatch 类位于 java.util.concu ...

  3. 24.多线程(等待唤醒机制,volatile,CAS 算法,线程池,定时器,设计模式)

    1.线程间的等待唤醒机制 Object 类中   void wait ()  在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待.         ...

  4. CAS算法-实现原理

    目录 CAS是什么? CAS解决了什么问题? CAS存在什么问题? CAS有哪些应用场景? cas的实现 最后 CAS是什么? CAS的全称为Compare and swap 比较并交换.CAS又经常 ...

  5. 面试:CAS算法原理

    1.什么是CAS? CAS:Compare and Swap,即比较再交换. jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronou ...

  6. CAS算法与ABA问题

    锁是用来做并发最简单的方式,当然代价也是最高的. 独占锁是一种悲观锁,synchronized就是一种独占锁:它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线 ...

  7. CAS算法的理解及应用

    应用 原子操作类,例如AtomicInteger,AtomicBoolean - 适用于并发量较小,多cpu情况下: Java中有许多线程安全类,比如线程安全的集合类.从Java5开始,在java.u ...

  8. 统计学习三要素 模型+策略+算法

    统计学习方法都是由模型. 策略和算法构成的. 即统计学习方法由三要素构成, 可以简单地表示为:方法=模型+策略+算法 模型 统计学习首要考虑的问题是学习什么样的模型. 在监督学习过程中, 模型就是所要 ...

  9. 统计学习的三个招式:模型、策略和算法

    统计学习的三个招式:模型.策略和算法 https://mp.weixin.qq.com/s/12yhAZ79i_ENAdtyOX63lQ 李航老师在统计学习方法中讲到:方法=模型+策略+算法 可以说模 ...

最新文章

  1. 【面试】我是如何在面试别人Spring事务时“套路”对方的
  2. 在应用了皮肤的程序中制作透明的文本编辑控件(如:TcxMemo)
  3. 洛谷 P1149 火柴棒等式
  4. 几个关于财报的基本知识
  5. C语言文件读写(2)-文本文件写操作
  6. 深入分析Synchronized原理(阿里面试题)
  7. spring事务管理-演示事务的环境准备
  8. java 快速排序 递归_Java递归快速入门
  9. OPPO宣布与哈苏达成影像战略合作
  10. Debian中proftpd+mysql+虚拟用户+匿名用户+磁盘限额的配置
  11. How fast is a C++ extension by the PHP-CPP liberary?
  12. python图片转excel_利用python将图片转换成excel文档格式
  13. python柱状图显示数值_Python实现绘制双柱状图并显示数值功能示例
  14. 【OpenGL】斯坦福兔子、显示列表
  15. Apache POI Excel固定(冻结)单元格
  16. C#开发实战视频教程_基于多线程C#开发QQ农场
  17. Rstudio如何安装、加载工具包
  18. 【生日碰撞和数字签名】
  19. arraycoy java,LifeAsia 功能通过Java转移到Web
  20. 智驾科技招聘|SLAM算法总监、感知定位、高精地图等岗位(20~50K)

热门文章

  1. 使用sqlplus创建表空间
  2. 汽车厂商集体大降价,谁更受伤?
  3. 记住这三个方法,让你的钱越花越多
  4. 将open目录及子目录和文件删除
  5. 大二学科不挂科 速冲方法推荐(数据结构+计组+操作系统+算法+数据库+计网)
  6. Spring Boot使用@RepeatSubmit 防止重复提交
  7. 13个可以激励自己的名言
  8. Oracle-索引、视图
  9. 编程语言排行榜2021年3月
  10. 2017 ACM Arabella Collegiate Programming Contest