理解Java并发里面的CAS概念

前言

我们知道在Java多线程里面关于共享变量的操作,一定是要使用线程同步来保证线程安全的,一旦涉及线程同步,就需要加锁,一旦加锁就意味着某一个时候只能有一个线程在操作,其他的线程如果没有得到锁就会阻塞起来,此时的线程的状态是BLOCKED,当前面的线程释放锁的时候,系统会自动调度当前的线程进入临界区,这里面存在一个问题,就是线程的上下文切换的问题,虽然比起来进程的上下文切换,线程的上下文切换更轻量级,但仍然也是有一定开销的,比如最简单的i++的例子,那么如何有没有一种不需要加锁也能保证线程安全的数据结构呢?答案是肯定的,这就是今天需要谈到的CAS(Compare And Swap或 Compare And Set)。

什么是CAS

CAS通常是指Compare And Swap或 Compare And Set)是硬件操作系统级别提供的具有原子性的原语指令,利用它可以在多线程中取得和同步一样的效果。

CAS的原理

CAS 算法大致原理是:在对变量进行计算之前(如 ++ 操作),首先读取原变量值,称为 旧的预期值 A,然后在更新之前再获取当前内存中的值,称为 当前内存值 V,如果 A==V 则说明变量从未被其他线程修改过,此时将会写入新值 B,如果 A!=V 则说明变量已经被其他线程修改过,当前线程应当什么也不做或者再循环几个周期直到更新成功。

CAS通过避免同步,也就是避免了线程的上下文来极大的提高了同步处理的能力,比如在多线程下使用AtomicLong的无锁累加要比,使用synchronized关键字处理性能高出很多。

CAS 缺点

(1)ABA问题

通过上面的解释,我们知道CAS的原理是读取两个时刻的值,然后比较是否一致再决定是否更新,如果不一致,那么就需要多循环几次直到更新成功,这里面有一个问题假如第一次读到的预期值是A,然后在这段时间间隔内A的值变化成B又变化成A,这个时候其实它的值已经被更新过了,但是如果只比较值是没法判断出来是否更新过的,虽然对于一些计数,求和的操作不影响结果,但这是有缺陷的。 解决方法:通过版本号来判断,在每次更新后对应的版本号加1,这样一来在比较值之后如果相等,可以再次比较版本号来判断是否真的更新过。

JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是 首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

(2)自旋多次循环导致的效率问题

上面说过CAS在判断两次读取的值不一样的时候会放弃操作,但为了保证结果正确,通常都会继续尝试循环再次发起CAS操作,如果连续多次CAS都失败,那么就会消耗大量的cpu资源。

(3)仅仅保证单个共享变量的原子操作?

CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效;不过从 JDK 1.5开始提供了 AtomicReference 类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作

CAS与Java并发工具包的关系

java.util.concurrent包里面的工具类基本全部都是使用了CAS+volatile的乐观锁机制,也就是说Java并发工具包里面的大多数类底层是构建在CAS+volatile这种lock-free的机制上,所以拥有更好更高的并发效率。

总结

CAS是一项基于乐观锁的非阻塞技术(不需要线程的上下文切换),虽然CAS也有自己的一些缺点,但其通过操作系统底层提供的原子指令来实现lock-free的取得同步效果,在大多数情况下其效率和性能都要比synchronized和lock更好。

原文发布于微信公众号 - 我是攻城师(woshigcs)

原文发表时间:2018-07-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏H2Cloud

FFLIB C++ 异步&类型安全&printf风格的日志库

摘要       C++程序的调试一般有调试器、printf、日志文件三种。Linux下的调试器为gdb,关于gdb的使用甚至可以单独用一本书来说明,但是本章并...

48190
来自专栏晓晨的专栏

ASP.NET Core 使用UrlFirewall对请求进行过滤

15820
来自专栏蓝天

tcpdump命令格式

tcpdump采用命令行方式,它的命令格式为:   tcpdump [ -adeflnNOpqStvx ] [ -c 数量 ] [ -F 文件名 ]      ...

10320
来自专栏landv

烽火2640路由器命令行手册-01-基础配置命令

使用copy命令可以从tftp服务器读取文件到路由器,也可以将路由器文件系统中的某个文件写到TFTP服务器。

32420
来自专栏IT笔记

JAVA中CAS原理详解

在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: 在多线程竞争下,加锁、释放锁会导致比较多的上下文切...

70080
来自专栏软件测试经验与教训

LR关联知识点详解

在脚本回放过程中,客户端发出请求,通过关联函数所定义的左右边界值(也就是关联规则),在服务器所响应的内容中查找,得到相应的值,以变量的形式替换录制时的静态值,从...

14230
来自专栏SDNLAB

第五届SDN大赛初赛部分试题解题思路:基于ONOS的路径反转实现

作者简介:周正强,北京邮电大学未来网络实验室在读研究生,个人邮箱:857538065@qq.com

25430
来自专栏安富莱嵌入式技术分享

【RL-TCPnet网络教程】第13章 RL-TCPnet之TCP服务器

本章节为大家讲解RL-TCPnet的TCP服务器实现,学习本章节前,务必要优先学习第12章TCP传输控制协议基础知识。有了这些基础知识之后,再搞本章节会有事半功...

14830
来自专栏运维小白

1-3 CCNA

IP地址 是逻辑地址 用来确定一个网络中的一个节点,或者一个设备 两台主机通信,必须要有IP地址,32位二进制数,为了便于记忆,转换成10进制数,如 192....

366150
来自专栏移动端开发

Android学习--探究服务(一)

      服务(service)是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖任...

10510

扫码关注云+社区

领取腾讯云代金券