博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 基本功 之 CAS
阅读量:6714 次
发布时间:2019-06-25

本文共 3101 字,大约阅读时间需要 10 分钟。

hot3.png

本文首发于个人公众号《andyqian》, 期待你的关注!

前言

  在Java并发编程中,我们经常使用锁对竞争资源予以并发控制,以解决资源竞争的问题。但无论是使用 Lock 还是 Synchronized,随着锁机制的引入,就不可避免的带来另一个问题,也就锁与解锁时的上下文切换,线程等待 等性能问题。现在回过头来看,在有些场景中,是否真的需要引入锁才能解决竞争资源共享问题?答案是否定的,在JDK源码中,也为我们实现了。就是今天要介绍的另外一种无锁方案-CAS,它大量应用于JUC 包中,也是atomic包中各类的底层原理,其重要行可想而知。

CAS 简介

  CAS 全称为:Compare And Swap (比较与替换),其核心思想是:将内存值 Value 与期望值 A 进行比较,如果两者相等,则将其设置为新值 B,否则不进行任何操作。CAS操作非常高效,在我看来,其原因有二,其一:底层调用的是 sun.misc.Unsafe 类,操作的是内存值,非常高效。其二:在多线程环境下,始终只有一个线程获得执行权,未获得执行权的线程并不会挂起而造成阻塞,而是以操作CAS失败后再次执行CAS操作,直至成功,这一个过程,在Java中称之为 “自旋”。

源码解析

  Java 中 CAS 应用的十分广泛,幕后英雄是sun.misc.Unsafe 类,单独看Unsafe类的CAS操作可能有些茫然,以我们熟悉的 AtomicInteger 类中的 compareAndSet 方法为引子,再分析到 Unsafe类可能会更好些。

下面为AtomicInteger 类中compareAndSet 方法的源码,如下所述:

12
public final boolean compareAndSet(int expect, int update) {    return unsafe.compareAndSwapInt(this, valueOffset, expect, update)}

 

方法入参中 expect 为期望值, update 为待更新值。

继续往下看,compareAndSet方法内部使用到的是Unsafe.compareAndSwapInt()方法,如下所述:

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

 

方法入参有四个,其中:

  1. Object var1 为对象。
  2. long var2 为 var1 对象的内存地址。
  3. int var4 为 内存地址 中的期望值。
  4. var5 为 待更新的值。

在Unsafe类中,同类的方法有以下几个:

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

其实 Unsafe 类还给我们提供了一系列底层的API,由于篇幅原因,就不再展开说明,下次放单独一篇文章中谈谈。

ABA 问题

   在 CAS 中有一个特别经典的问题,也就是ABA。它说的是:内存值 Value 与期望值 A 进行比较前,Value已经发生过变化了,只不过是其变化后的值也为Value。从而造成从结果上看,其结果一致是一致的,(多发生于多线程条件下)当然这也是符合CAS 条件的。在大多数场景下,我们并不需要关心这种场景,在需要关心时,我们也可以使用JDK为我们提供了实现类 - AtomicStampedReference。在 AtomicStampedReference 类中,引入了标记位的概念,用于标记value值是否被修改过。结合value值 + 标记位是否一致,来判断value值是否修改过。

其源码如下:

123456789101112
public boolean compareAndSet(V  expectedReference, // 期望引用对象                                V newReference,    // 新的引用对象                                int expectedStamp, //期望标志位                                int newStamp)  // 新的标识位       Pair
current = pair; // 获取对象与标识引用对 return expectedReference == current.reference && // 期望对象引用是否等于当前引用对象 (是否发生变化) expectedStamp == current.stamp&& // 期望stamp 是否等于当前stamp ((newReference == current.reference && //新的引用对象是否等于当前引用对象,新的stamp是否等于当前stamp newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); //进行pair 的cas操作

 

其中 pair 为 对象引用与版本标记对象,其源码如下:

1234567891011
private static class Pair
{ final T reference; final int stamp; private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static
Pair
of(T reference, int stamp) { return new Pair
(reference, stamp); } }

 

结语

  在Java 中 CAS 应用的十分广泛,包括但不限于:Atomic,synchorized 底层原理等等。但需要明确的是 CAS 的存在并不是用来替换 Lock 的,而是一种互补的关系。平常都在写业务代码,没有更深层次的查看源码,当查看源码时,却又是一件趣事,蛮好的!

 


相关阅读:

1.《》

2.《》
3.《》
4.《》

 扫码关注,一起进步

个人博客: 

 

转载于:https://my.oschina.net/u/1462914/blog/3039713

你可能感兴趣的文章
大数据助推新型智库建设
查看>>
新加坡欲重组通信和媒体管制机构
查看>>
《CCNP ROUTE 300-101学习指南》——2.2节构建EIGRP拓扑表
查看>>
Libreboot 项目向开源社区示好和致歉
查看>>
Linux Kernel 4.9-rc8,4.9 分支最后一个候选版
查看>>
《Web异步与实时交互——iframe AJAX WebSocket开发实战》—— 2.2 相关关键技术及工作原理...
查看>>
《Nmap渗透测试指南》—第1章1.5节Mac OS安
查看>>
学习和使用 PHP 应该注意的10件事
查看>>
.NET Framework 源码
查看>>
ArrayList源码分析
查看>>
JS Object的静态方法汇总( 上 )
查看>>
优朋普乐:OTT正重构电视版图
查看>>
Ubuntu 14.04 LTC 有线网络——网线不识别,灯不亮问题
查看>>
21_css布局2_浮动布局.html
查看>>
DateUtils 单元下的公用函数目录
查看>>
jQuery 练习[二]: 获取对象(1) - 基本选择与层级
查看>>
Sublime Text 2 快捷键用法大全
查看>>
用U盘安装debian系统
查看>>
SequoiaDB 笔记
查看>>
lduan HyPer-V 网络存储(三)
查看>>