乐观锁和悲观锁是两种不同的解决并发问题的策略。悲观锁策略假定任何一次并发都会发生冲突,所以总是采用最严格的方式来进行并发控制。java中的独占锁(synchronized和重入锁)就是典型悲观锁实现,它只允许线程互斥的访问临界区,也就是阻塞式的同步方式。而乐观锁策略假定大部分情况下并发冲突不会发生,采用的是一种更为宽松的方式来进行并发控制。比如我们马上就要讲的CAS操作。它允许多线程非阻塞式地对共享资源进行修改,但同一时刻只有一个线程能够成功,其他线程被告知失败但并不会挂起,而是重新尝试。这是一种非阻塞式的同步方式。

2. CAS详解

Java中的CAS操作依赖于底层CPU的CAS指令。

2.1 CAS指令

CAS,即Compare-and-Swap(比较和交换),从语义上它需要两次操作,但只需要一条cpu指令就能完成,因而该操作具有原子性,像原子一样不可分割,要么成功,要么失败。

CAS指令需要3个操作数,分别是V:变量的内存地址,A(预期值),B(更新值)。CAS指令执行时,只有当预期值A和V的值一样时才进行更新,否则更新失败。

2.3 Java中的CAS指令

java中给我们提供了本地方法来获得和CAS指令一样的执行效果。比如Unsafe类中

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

因为这些方法会被编译成平台相关的CAS指令,故而这些CAS操作都具有原子性。遗憾的是,这些CAS操作我们无法直接使用,因为只有Bootstrap ClassLoader加载的Class才能访问它。然而在JDK并发包的底层实现中,还是可以处处看到它的身影。如下图所示

2.4 CAS结合失败重试机制进行并发控制

CAS指令只是提供了一个更新变量的原子操作,要使用它进行并发控制,还需要结合失败重试机制。以AtomicInteger为例,对它进行累加操作是线程安全的,而普通的整型变量在多线程环境下执行类似i++的操作线程不安全。为什么?因为i++并非是一个原子操作,而AtomicInteger类的getAndIncrement底层的CAS操作是原子性的,故而能保证线程安全。下面是它的源码(基于JDK8)

/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

进入方法Unsafe类的getAndAddInt方法,

public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2); //获得变量当前的值
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); //CAS失败则重新尝试直到成功为止 return var5;
}

可以看到步骤可以概括为

  • 1.获得变量当前的值var5
  • 2.使用CAS操作进行更新:若var5与当前的值不一样,说明1,2操作间有其他线程作了修改,此次更新失败,重新实行步骤1;否则用 var5+var4的值进行更新,并返回更新前的值。




    多个线程同时使用CAS指令去更新变量,失败的线程将会不断重新尝试,直到更新成功。

3. CAS操作的优势和劣势

3.1 CAS相比独占锁的优势

  • 没有线程阻塞唤醒带来的性能消耗问题。

3.2 CAS的缺点

  • ABA问题。在CAS操作时,我们以变量的当前值和预期值一致来判定变量未被其他线程修改,这样是不用严谨的,因为变量可能被修改成其他值后又被改了回来,大部分时候这是个可以忽略的小问题,如果要规避这个问题,可以使用AtomicStampedReference,它会额外使用一个时间戳来判断变量是否被修改过。
  • 无法直接使用CAS来进行并发控制,相比同步锁的方式适用范围较窄。

4. 总结

CAS是CPU的一条指令,用来对变量进行原子更新。java中使用CAS技术结合失败重试机制,可以非阻塞的实现多线程对共享资源的并发修改,很多时候具有比独占锁更好的性能。

无锁的同步策略——CAS操作详解的更多相关文章

  1. 无锁机制----比较交换CAS Compare And Swap

    一.锁与共享变量 加锁是一种悲观的策略,它总是认为每次访问共享资源的时候,总会发生冲突,所以宁愿牺牲性能(时间)来保证数据安全. 无锁是一种乐观的策略,它假设线程访问共享资源不会发生冲突,所以不需要加 ...

  2. [Android新手区] SQLite 操作详解--SQL语法

    该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法  :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解 ...

  3. 剑指Offer——线程同步volatile与synchronized详解

    (转)Java面试--线程同步volatile与synchronized详解 0. 前言 面试时很可能遇到这样一个问题:使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现 ...

  4. Linux Shell数组常用操作详解

    Linux Shell数组常用操作详解 1数组定义: declare -a 数组名 数组名=(元素1 元素2 元素3 ) declare -a array array=( ) 数组用小括号括起,数组元 ...

  5. memcached 命令操作详解

    memcached 命令操作详解 一.存储命令 存储命令的格式: <command name> <key> <flags> <exptime> < ...

  6. MySQL 操作详解

    MySQL 操作详解 一.实验简介 本节实验中学习并实践 MySQL 上创建数据库.创建表.查找信息等详细的语法及参数使用方法. 二.创建并使用数据库 1. 创建并选择数据库 使用SHOW语句找出服务 ...

  7. SVN的Windows和Linux客户端操作详解

    SVN的Windows和Linux客户端操作详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Windows客户端操作 1.安装SVN客户端 a>.去官网下载svn软件 ...

  8. VC++常用数据类型及其操作详解

    原文地址:http://blog.csdn.net/ithomer/article/details/5019367 VC++常用数据类型及其操作详解 一.VC常用数据类型列表 二.常用数据类型转化 2 ...

  9. Python对Excel操作详解

      Python对Excel操作详解 文档摘要: 本文档主要介绍如何通过python对office excel进行读写操作,使用了xlrd.xlwt和xlutils模块.另外还演示了如何通过Tcl   ...

随机推荐

  1. [置顶] 【机器学习PAI实践六】金融贷款发放预测

    一.背景 很多农民因为缺乏资金,在每年耕种前会向相关机构申请贷款来购买种地需要的物资,等丰收之后偿还.农业贷款发放问题是一个典型的数据挖掘问题.贷款发放人通过往年的数据,包括贷款人的年收入.种植的作物 ...

  2. Getting started with Android and Kotlin (译文)

    原文链接 http://kotlinlang.org/docs/tutorials/kotlin-android.html 写在前面 Kotlin 是一个基于 JVM 的新的编程语言,由 JetBra ...

  3. prefixfree.min.js 的用途

    简单而言,这东西就是自动加前缀的,在link中,或是style中,或是dom元素的style中书写CSS3 code,或是jQuery .css()方法此脚本会自动补上需要的前缀,让响应的浏览器支持该 ...

  4. android知识点大总结

    1.掌握Android编程的基本概念与要点,Android SDK及其开发环境搭建.Android项目结构分析.2.Android 应用设计模式.文件系统.3.文件形式的数据存储与访问.SDCard卡 ...

  5. LAMP环境运行中为PHP添加CURL模块

    这里是自己遇到的问题记录并总结 1.—— : LAMP环境所需源码包在 /websrc 下 [保存了WEB环境所需的各种tar.gz 源码包]命名为资源目录 2.—— : LAMP环境源码包统一解压到 ...

  6. 【MFC】vs2013_MFC使用文件之15.mfc 按钮CBitmapButton的使用

    本文是基于对话框的 博文基于 无幻 的博文为基础写的 http://blog.csdn.net/akof1314/article/details/4951836 笔者使用mfc撑死2个星期,不过这是有 ...

  7. HDU - 233 Matrix

    原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=5015 解题思路:一看到题目,感觉是杨辉三角形,然后用组合数学做,不过没想出来怎么做,后来看数据+递推思 ...

  8. Linux下更改oracle客户端字符集和服务端字符集

    from:http://blog.csdn.net/chid/article/details/6166506 Linux 下更改 oracle 客户端字符集和服务端字符集 1.Linux 下更改 or ...

  9. FastAdmin 导入 Excel 相关资料收集 (2018-08-14)

    FastAdmin 导入 Excel 相关资料收集 新版本一键CRUD后自带导入功能,但是默认被禁用,如何启动 https://forum.fastadmin.net/thread/540 Excel ...

  10. 错过的sql语句

    总结: 内链接:适合和自己的条件对比,但并没有给出具体条件,要从数据库表里面找,注意有些条件两个表都需要写(嵌套查询貌似也可以 左连接:适合一个表要全部列出来的情况(使用count的时候,注意coun ...