前言

在高并发的应用当中,最关键的问题就是对共享变量的安全访问,通常我们都是通过加锁的方式,比如说synchronized、Lock来保证原子性,或者在某些应用当中,用voliate来保证变量的可见性,还有就是通过TheadLocal将变量copy一份,称为局部变量(线程私有)等等。现在我们学习一种不加锁机制(CAS)

上述我们提到的synchronized和Lock这都是通过加锁实现的(悲观锁),其实加锁本质上是将并发转变成串行实现的,势必会阻塞线程的执行,影响应用的吞吐量,而CAS正是一种乐观的策略,并不会出现加锁来阻塞线程的执行。

CAS简介

CAS,Compare And Swap,即比较并交换。Atomic原子类操作等等都是以CAS实现的,还有ConcurrentHashMap在JDK1.8的版本也调整为了CAS+Synchronized

分析(自旋中对应下文的native方法)

在CAS中有三个参数:内存值V、旧的预期值A、要更新的值B。当且仅当内存值V的值等于旧的预期值A时才会将内存值V的值修改为B,否则什么都不干

伪代码如下:

if(this.value == A){
this.value = B
return true;
}else{
return false;
}

应用

在java.util.concurrent.atomic包下原子类都是通过CAS来实现的,现在我们以AtomicInteger为例来分析一下CAS的实现

    private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value;

Unsafe是CAS的核心类,Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM还是开了一个后门:Unsafe,它提供了硬件级别的原子操作。

valueOffset为变量值在内存中的偏移地址,unsafe就是通过偏移地址来得到数据的原值的。

value当前值,使用volatile修饰,保证多线程环境下看见的是同一个。

下面我们以AtomicInteger的addAndGet()方法来说明

    public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}

内部调用的是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)); return var5;
}

而在getAndAddInt方法中又调用了Unsafe的本地方法

public native int getIntVolatile(Object var1, long var2);

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

此方法是本地方法(java需要调用其他语言的代码,比如C语言代码,跟dll文件有关),有四个参数,分别代表:对象、对象的地址、预期值、修改值

CAS的缺陷

  • 乐观锁只能保证一个共享变量的原子操作。如上例子,自旋过程中只能保证value变量的原子性,这时如果多一个或几个变量,乐观锁将变得力不从心,但互斥锁能轻易解决,不管对象数量多少及对象颗粒度大小。

  • 长时间自旋可能导致开销大。假如CAS长时间不成功而一直自旋,会给CPU带来很大的开销。

  • ABA问题。CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。

synchronized与CAS的区别

  • 对于资源竞争较少的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。

  • 对于资源竞争严重的情况,CAS自旋的概率会比较大(比如getAndAddInt方法中的do-while循环),从而浪费更多的CPU资源,效率低于synchronized。

java并发之CAS详解的更多相关文章

  1. Java并发之AQS详解

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  2. Java并发之AQS详解(转)

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  3. Java并发之ReentrantLock详解

    一.入题 ReentrantLock是Java并发包中互斥锁,它有公平锁和非公平锁两种实现方式,以lock()为例,其使用方式为: ReentrantLock takeLock = new Reent ...

  4. java并发之synchronized详解

    前言 多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本身. 同 ...

  5. Java并发之CompletionService详解

    CompletionService是什么? 它是JUC包中的一个接口类,默认实现类只有一个ExecutorCompletionService. CompletionService干什么的? 它将异步任 ...

  6. Android开发之InstanceState详解

    Android开发之InstanceState详解   本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceS ...

  7. Android开发之InstanceState详解(转)---利用其保存Activity状态

    Android开发之InstanceState详解   本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceS ...

  8. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  9. Android开发之MdiaPlayer详解

    Android开发之MdiaPlayer详解 MediaPlayer类可用于控制音频/视频文件或流的播放,我曾在<Android开发之基于Service的音乐播放器>一文中介绍过它的使用. ...

随机推荐

  1. 【Offer】[5] 【替换空格】

    题目描述 思路分析 Java代码 代码链接 题目描述 请实现一个函数,把字符串中的每个空格替换成"%20". 例如输入"We are happy.",则输出&q ...

  2. mybatis 批量插入时候的一个注意点

    <insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys=&quo ...

  3. SpringBoot应用启动过程分析

    真的好奇害死猫!之前写过几个SpringBoot应用,但是一直没搞明白应用到底是怎么启动的,心里一直有点膈应.好吧,趁有空去看了下源码,写下这篇博客作为学习记录吧! 个人拙见,若哪里有理解不对的地方, ...

  4. redis之pipeline使用

    redis之pipeline 我们要完成一个业务,可能会对redis做连续的多个操作,这有很多个步骤是需要依次连续执行的.这样的场景,网络传输的耗时将是限制redis处理量的主要瓶颈. 那么此时就可以 ...

  5. 明明有class为什么还是报ClassNotFoundException?

    描述 我们修改接口时,习惯发布一个快照版本用于测试.我们的一个服务也是发布了快照版本,然后一个jar程序要依赖这个服务,修改pom文件打包部署后,通过 java -jar 命令执行这个jar程序,然后 ...

  6. Airflow:TypeError an integer is required (got type NoneType) 一次诡异问题排查

    ​ 当使用rabbitmq作为airflow的broker的时候,启动scheduler,即执行airflow scheduler命令的时候抛出以下异常: Traceback (most recent ...

  7. 【学习笔记】python3核心技术与实践--开篇词

    python的应用和流行程度: Python 可以运用在数据处理.Web 开发.人工智能等多个领域,它的语言简洁.开发效率高.可移植性强,并且可以和其他编程语言(比如 C++)轻松无缝衔接.现如今,不 ...

  8. airflow的安装

    1.环境准备1.1 安装环境1.2 创建用户2.安装airflow2.1 安装python2.2 安装pip2.3 安装数据库2.4 安装airflow2.4.1 安装主模块2.4.2 安装数据库模块 ...

  9. Java面试-interrupt

    我们都知道,Java中停止一个线程不能用stop,因为stop会瞬间强行停止一个线程,且该线程持有的锁并不能释放.大家多习惯于用interrupt,那么使用它又有什么需要注意的呢? interrupt ...

  10. css禁止选中文字

    很简单: -moz-user-select:none;/*火狐*/ -webkit-user-select:none;/*webkit浏览器*/ -ms-user-select:none;/*IE10 ...