并发编程时,对于共享资源的使用需要确保绝对的安全性。除了利用锁机制之外,还有一种无锁的概念。所谓无锁,就是假定在并发情况下,对于共享资源的访问没有冲突,线程可以一直不停的运行,无需阻塞,如果产生冲突,则使用CAS算法确保安全性。Java在很多并发代码中都使用了这种算法。

  CAS算法的核心参数如下:

compareAndSet(V,E,A)

  V代码需要进行更新的变量;E代表预期值;A代表所要更新的值。

  CAS的核心思想就是:当要对一个变量进行更新时,先取出该变量此时在内存中的实际值,与预期值进行比较。如果相等,则代表没有其他线程对其进行过修改,直接更新。如果不同,表示有其他线程修改过,此时有两种策略。一种是让CAS自旋,直到更新成功;另外表示当前线程更新失败,继续后续逻辑。大概示意图如下:

  对于CAS自旋,有可能会出现长时间更新失败,浪费CPU性能的情况。JDK1.6以后,加入了适应性自旋的概念。即如果某个锁自旋时很少获得成功,那么会减少自旋次数。自旋次数的默认值是10次,可以使用参数-XX:PreBlockSpin来更改。

  再说一下为什么CAS在更新变量值的时候不会受到其他线程的影响呢?会不会我在判断更新值与预期值相等,进行更新的过程中,变量被其他线程更新了呢?其实不会,因为CAS具有排它性,一次CAS是一个原子操作,是CPU源语级别。确保隔离性。

  Java对于CAS是使用Unsafe类进行支持的。顾名思义,不安全,可以让程序员像C语言那样直接操纵内存指针。java.util.concurrent.atomic包下的类,都是使用CAS实现的。

ABA缺陷

  不知道大家通过上面对CAS的介绍说明,看到了CAS算法的缺陷没有!

  CAS本身会有ABA问题。举个例子,变量m = 10,我要把m更新为30。在我判断之前,有其他线程先把m更新到50,在从50更新到10。那么我进行 10 == 10的判断时,这时候我怎么确定m的值有没有被改过呢?这就是ABA问题了。

  要解决这个问题,我们就需要在进行 10 == 10判断的时候,还需要有其他参照。Java中有一个类AtomicStampedReference,这个类除了维护变量值之外,还会维护一个时间戳。用于确定数值版本。

private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
} private volatile Pair<V> pair;

  用volatile关键字修饰的内部类。

  

  看一下核心的compareAndSet()方法:

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)));
}

  可以看到除了比较数值之外,还会比较时间戳。

最新文章

  1. 小丁带你走进git的世界三-撤销修改
  2. JavaWeb开发环境准备之Linux篇
  3. 2016中国大学生程序设计竞赛 - 网络选拔赛 C. Magic boy Bi Luo with his excited tree
  4. 【转载】C/C++中#ifdef和#endif的用法
  5. json(2)
  6. AMQ学习笔记 - 15. 实践方案:基于ActiveMQ的统一日志服务
  7. qt 5 小练习 创建无边框界面
  8. web前端工程师必须掌握的localStorage(二)
  9. POJ3026 Borg Maze(最小生成树)
  10. python闭包以及装饰器
  11. ssh密码
  12. IDEA之Jrebel插件激活
  13. tomcat中间件提交表单数据量过大警告处理方案
  14. JavaScript数据类型之数字类型
  15. 【洛谷4770】 [NOI2018]你的名字(SAM,线段树合并)
  16. [Android Pro] 开发一流的 Android SDK:Fabric SDK 的创建经验
  17. 【Unix网络编程】chapter3 套接字编程简介
  18. Mybatis笔记六:Mybatis中SqlSessionFactoryBuilder/SqlSessionFactory/SqlSession/映射器实例的作用域(Scope)和生命周期
  19. pytest文档18-配置文件pytest.ini
  20. 【Struts2学习笔记(9)】单文件上传和多文件上传

热门文章

  1. php 输出缓冲
  2. bzoj5178 [Jsoi2011]棒棒糖 主席树+线段树二分
  3. 线程中sleep和wait方法的区别
  4. FreeRTOS之taskYIELD()
  5. 【leetcode】1041. Robot Bounded In Circle
  6. matplotlib.pyplot 包
  7. read_ila
  8. r_action
  9. JS-让浏览器兼容ES6特性
  10. Angular.js路由 简单小案例