一、前言

  1. cas 一般认为是compare and swap 也可以认为是compare and set

  2. cas涉及三个值

  3. 1)P 变量内存地址

    2)E 期望值 ,CPU做计算之前拿出来的旧值

    3) X 需要设置的新值

原子操作为: 拿出内存地址当前的值A ,比较A == E ? 是 : 设置P内存的值为X 否:结束。。失败

  1. (1) 第一篇 话说synchronized 画过CAS的流程图 咱们再来一张?

    (2) CAS面试经常问的一个是ABA 问题 什么是ABA ? 上图

(3) 有人说ABA 不影响啊 我反正期望的值是A 你最后是A就得了呗

​ 这个还要看具体的业务,拿生活中例子来说,银行职员小孙,偷拿了银行100万,

然后去投资赚了20万,最后把100万还回去。 你细品。。 银行能允许吗

(4)ABA 的解决方案 版本 version 怎么解决 ?

二、DEMO

1. CAS 简单使用

假如有一个值 int count ,2个线程 每个线程给count加5000次 1

按道理说 每个人给你5000 你应该有1万块

public class CasTest {
public static int count = 0;
public static void main(String[] args) throws InterruptedException {
new Sub("第一个").start();
new Sub("第二个").start(); TimeUnit.SECONDS.sleep(5);
System.out.println("count="+CasTest.count);
}
} class Sub extends Thread{
private String name;
public Sub(String name) {
this.name = name;
}
public void run() {
System.out.println(name+"开始+");
for (int i = 0; i < 5000; i++) {
try {
CasTest.count = CasTest.count+1;
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(name+"加完了..");
}
} 执行结果:
第一个开始+
第二个开始+
第一个加完了..
第二个加完了..
count=6811

解决方案1 加锁synchronized 或者 lock 都可以

public class CasTest02 {
public static Integer count = 0;
public static void main(String[] args) throws InterruptedException {
new Sub02("第一个").start();
new Sub02("第二个").start(); TimeUnit.SECONDS.sleep(5);
System.out.println("count="+CasTest02.count);
}
} class Sub02 extends Thread{
private String name;
public Sub02(String name) {
this.name = name;
}
public void run() {
System.out.println(name+"开始+");
for (int i = 0; i < 500; i++) {
try {
// 加锁
synchronized (CasTest02.class) {
CasTest02.count = CasTest02.count+1;
}
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(name+"加完了..");
}
}

​ 解决方案2: CAS java自带的原子类 AtomicInteger

​ 读者读到这里可以了解一下LongAdder

public class CasTest03 {
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
new Sub03("第一个").start();
new Sub03("第二个").start(); TimeUnit.SECONDS.sleep(5);
System.out.println("count="+CasTest03.count);
}
} class Sub03 extends Thread{
private String name;
public Sub03(String name) {
this.name = name;
}
public void run() {
System.out.println(name+"开始+");
for (int i = 0; i < 5000; i++) {
// 加锁
CasTest03.count.incrementAndGet();
}
System.out.println(name+"加完了..");
}
}
执行结果:
第一个开始+
第二个开始+
第二个加完了..
第一个加完了..
count=10000

2. ABA 问题

这里简单复现一个ABA问题 可能不是很精确 读者朋友体会意思即可

public class CasTest04 {
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
new Thread(()->{ count.compareAndSet(0,1);
count.compareAndSet(1,0);
System.out.println("线程1 把count从0 修改为1 再从1 修改为0 ");
},"线程1").start(); new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
// 这里是0 但是已经不是他所希望的那个0 了
count.compareAndSet(0,4);
System.out.println("线程2 把count 从0 修改为4");
} catch (Exception e) {
e.printStackTrace();
}
},"线程2").start(); TimeUnit.SECONDS.sleep(5);
System.out.println("count="+count);
}
}
执行结果:
线程1 把count从0 修改为1 再从1 修改为0
线程2 把count 从0 修改为4
count=4

ABA解决 加版本

public class CasTest05 {
public static AtomicStampedReference<Integer> count = new AtomicStampedReference<>(0,0);
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
try {
// 等1秒 让线程2拿到版本
TimeUnit.SECONDS.sleep(1);
boolean res = count.compareAndSet(
0,
1,
count.getStamp(),
count.getStamp() + 1);
boolean res2 = count.compareAndSet
(1,
0,
count.getStamp(),
count.getStamp() + 1);
System.out.println("线程1 把count从0 修改为1 再从1 修改为0 "
+ ( res2 ? "成功!":"失败!"));
}catch (Exception r){
r.printStackTrace();
} },"线程1").start(); new Thread(()->{
try {
// 版本
int stamp = count.getStamp();
TimeUnit.SECONDS.sleep(2);
// 这里是0 但是已经不是他所希望的那个0 了 版本已经变了
boolean res = count.compareAndSet(0,4,stamp,stamp+1);
System.out.println("线程2 把count 从0 修改为4"
+ ( res ? " 成功!":" 失败!"));
} catch (Exception e) {
e.printStackTrace();
}
},"线程2").start(); TimeUnit.SECONDS.sleep(5);
System.out.println("count="+count.getReference());
}
}

三、 假装学术讨论

/**
* @author 木子的昼夜
*/
public class CasTest {
public static void main(String[] args) {
// JUC包里的原子类 线程安全的
AtomicInteger ai = new AtomicInteger();
// 加1 并返回
Integer res = ai.incrementAndGet();
System.out.println(res); // 上边这句话的意思相当于
int i = 0;
i = i+1;
int resi = i;
System.out.println(resi);
}
} /**
* Atomically increments by one the current value.
* 当前值自动加1
* @return the updated value
* 返回更新了的值
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
} // Unsafe.java#getAndAddInt
public final int getAndAddInt(Object obj, long offset, int step) {
int E;
do {
var5 = this.getIntVolatile(obj, offset);
// 这里不一定能一次就成功哦 会执行多次
// 跟一个姑娘表白 一次不成 你就灰溜溜走了 ? 活该单身..
} while(!this.compareAndSwapInt(obj, offset, E, E + step)); return var5;
}
// Unsafe.java#getIntVolatile 这个方法是获 对象obj 内存开始地址 相对偏移位置offset 对应的值
// 说白了 就是获取对象对应字段的值
public native int getIntVolatile(Object obj, long offset); //
public final native boolean compareAndSwapInt(
Object obj, // 被修改属性的对象
long offset, // 被修改字段相对于当前对象内存首地址偏移量 可以通过他直接去内存拿数据
int E, // 期望值
int X);// 需要设置的新值
// obj设置属性的对象 offset 字段相对于类内存起始位置偏移量  e期望值  x要设置的值
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
// 转换对象格式 jobject- > oop
oop p = JNIHandles::resolve(obj);
if (p == NULL) {
// 获取filed对应的内存地址 对象起始地址+偏移量
volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset);
// 把addr内存对应的值 设置为x 前提是内存值要等于e
return RawAccess<>::atomic_cmpxchg(addr, e, x) == e;
} else {
//
assert_field_offset_sane(p, offset);
return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x) == e;
}
} UNSAFE_END
// 这里就调用了 atomic_cmpxchg: 系统方法 : 原子比较并交换计数值。
atomic_cmpxchg(void* addr, T compare_value, T new_value) {
if (is_hardwired_primitive<decorators>()) {
const DecoratorSet expanded_decorators = decorators | AS_RAW;
return PreRuntimeDispatch::atomic_cmpxchg<expanded_decorators>
(addr, compare_value, new_value);
} else {
return RuntimeDispatch<decorators, T, BARRIER_ATOMIC_CMPXCHG>::atomic_cmpxchg
(addr, compare_value, new_value);
}
}

再底层就是系统级别的实现了,CPU 实现Atomic ,我也是看文章看得,说是有2中方式

  1. 使用总线锁 总线就是老大 CPU小c 给总线发一个LOCK信号 总线收到之后 小c就独占共享内存了,其他CPU

    就没有使用权限了 ,数据夸缓存行时使用总线锁 这时候不能用缓存锁
  2. 使用缓存锁 大多数时候 我们只需要保证对某一块内存的操作时原子性即可,缓存锁就是如果内存区域被缓存再处理器的缓存行中,并且操作的时候缓存行被锁定了,那么当处理器计算完回写到内存时,处理器就把缓存行的地址修改了,如果这时有两一个处理器回写数据到缓存行,咦? 失效了。。

下边连接是我准备写文章的目录,如果有读者觉得需要添加,请微信公众直接发消息给我。

https://www.processon.com/view/link/5f57817963768959e2dc7dca

最后附上自己公众号刚开始写 愿一起进步:

注意: 以上文字 仅代表个人观点,仅供参考,如有问题还请指出,立即马上连滚带爬的从被窝里出来改正。

话说CAS的更多相关文章

  1. Java高性能编程之CAS与ABA及解决方法

    Java高性能编程之CAS与ABA及解决方法 前言 如果喜欢暗色调的界面或者想换换界面,可以看看我在个人博客发布的 Java高性能编程之CAS与ABA及解决方法. CAS概念 CAS,全称Compar ...

  2. 随意看看AtomicInteger类和CAS

    最近在读jdk源码,怎么说呢?感觉收获还行,比看框架源码舒服多了,一些以前就感觉很模糊的概念和一些类的用法也清楚了好多,举个很简单的例子,我在读Integer类的时候,发现了原来这个类自带缓存,看看如 ...

  3. .NET应用和AEAI CAS集成详解

    1 概述 数通畅联某综合SOA集成项目的统一身份认证工作,需要第三方系统配合进行单点登录的配置改造,在项目中有需要进行单点登录配置的.NET应用系统,本文专门记录.NET应用和AEAI CAS的集成过 ...

  4. SuperMap-iServer-单点登录功能验证(CAS)

    SuperMap-iServer-单点登录功能验证(CAS) 1.测试目的: 验证SuperMap-iServer使用CAS单点登录的功能是否正常. 2.测试环境: SuperMap-iServer8 ...

  5. 集成基于CAS协议的单点登陆

    相信大家对单点登陆(SSO,Single Sign On)这个名词并不感到陌生吧?简单地说,单点登陆允许多个应用使用同一个登陆服务.一旦一个用户登陆了一个支持单点登陆的应用,那么在进入其它使用同一单点 ...

  6. 【Java并发编程实战】-----“J.U.C”:CAS操作

    CAS,即Compare and Swap,中文翻译为"比较并交换". 对于JUC包中,CAS理论是实现整个java并发包的基石.从整体来看,concurrent包的实现示意图如下 ...

  7. 多线程同步工具——CAS原子变量

    这是我参考的一篇文章<基于CAS的乐观锁实现>,讲述的是一种需要CPU支持的执行技术CAS(Compare and Swap). 首先理解什么是原子性操作,意思是不能再拆分的操作,例如改写 ...

  8. CAS FOR WINDOW ACTIVE DIRECTORY SSO单点登录

    一.CAS是什么? CAS(Central Authentication Service)是 Yale 大学发起的一个企业级的.开源的项目,旨在为 Web 应用系统提供一种可靠的单点登录解决方法(支持 ...

  9. CAS Client集群环境的Session问题及解决方案

    [原创申明:文章为原创,欢迎非盈利性转载,但转载必须注明来源] 之前写过一篇文章,介绍单点登录的基本原理.这篇文章重点介绍开源单点登录系统CAS的登录和注销的实现方法.并结合实际工作中碰到的问题,探讨 ...

随机推荐

  1. 四、Jmeter 集合点(实际场景应用)

    一.jmeter集合点的作用域及作用范围 先明确一些概念:1)定时器是在每个sampler(采样器)之前执行的,而不是之后: 是的,你没有看错,不管这个定时器的位置放在sampler之后,还是之下,它 ...

  2. Linux系统编程【3.2】——ls命令优化版和ls -l实现

    前情提要 在笔者的上一篇博客Linux系统编程[3.1]--编写ls命令中,实现了初级版的ls命令,但是与原版ls命令相比,还存在着显示格式和无颜色标记的不同.经过笔者近两天的学习,基本解决了这两个问 ...

  3. Python_微信支付(云开发)

    一.创建云开发小程序 1.初始化云开发环境 //app.js App({ onLaunch: function () { wx.cloud.init({ //初始化云开发环境 env: 'wxypay ...

  4. oslab oranges 一个操作系统的实现 实验二 认识保护模式

    https://github.com/yyu/osfs00 实验目的: 理解x86架构下的段式内存管理 掌握实模式和保护模式下段式寻址的组织方式. 关键数据结构.代码组织方式 掌握实模式与保护模式的切 ...

  5. hash table

    Hash Table,叫做哈希表,也叫做散列表.概念:通过某种对应关系h,使得每一个元素和储存位置一一对应.这种对应关系称为哈希函数.它最大的优点就是插入.搜索和删除得很快(O(1)).碰撞(Coll ...

  6. universities

  7. PAT l2-010 排座位 【并查集】

    L2-010. 排座位 时间限制 150 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位. ...

  8. Github OAuth All In One

    Github OAuth All In One new https://docs.github.com/en/free-pro-team@latest/developers/apps/authoriz ...

  9. CSS will-change All In One

    CSS will-change All In One CSS animation effect live demo https://nextjs.org/conf/ https://nextjs.or ...

  10. ES6 Map vs ES5 Object

    ES6 Map vs ES5 Object Map vs Object https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referenc ...