一、CAS是什么?

比较并交换,它是一条CPU并发原语。

CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

java.util.concurrent.atomic中的AtomicXXX,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操作,在java.util.concurrent中的大多数类在实现时都直接或间接的使用了这些原子变量类。

原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题

public class CASDemo {

    public static void main(String[] args) {
//主物理内存的值默认是5
AtomicInteger atomicInteger = new AtomicInteger(5);
//如果线程的期望值与物理内存的真实值一样,就修改为更新值。
System.out.println(atomicInteger.compareAndSet(5,2019)+" current data:"+atomicInteger.get()); //t1线程的工作内存 变量的副本拷贝
System.out.println(atomicInteger.compareAndSet(5,1024)+" current data:"+atomicInteger.get()); //t2线程的工作内存 变量的副本拷贝
}
}

二、说说CAS底层原理?谈谈你对UnSafe的理解

要点:Unsafe类(存在rt.jar中)+CAS自旋锁

  • AtomicInteger的源码
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates
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;
}

1、Unsafe类

是CAS的核心类,由于java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。

Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作执行依赖于Unsafe类。

Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务  

2、变量valueOffset,表示该变量在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的。

//this:当前对象
//valueOffset:内存偏移量
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//var1 AtomicInteger对象本身
//var2 该对象值的引用地址
//var4 需要变动的值
//var5 用var1 var2找出主内存中真实的值
//用该对象当前值与var5比较,如果相同,更新var5+var4返回true,如果不同,继续取值比较,直到更新完成
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;
}  

总结:getAndIncrement()底层调用unsafe类方法,传入三个参数,unsafe.getAndAddInt() 底层使用CAS思想,如果比较成功加1,如果比较失败重新获得,再比较一次,直至成功。

3、变量value用volatile修饰,保证了多线程之间的内存可见性。

三、CAS缺点:

  • 循环时间长开销大(如果CAS失败,会一直尝试)
  • 只能保证一个共享变量的原子操作。(对多个共享变量操作时,循环CAS无法保证操作的原子性,只能用加锁来保证)
  • 存在ABA问题

四、原子类AtomicInteger类ABA问题及解决方案

1、ABA问题是怎么产生的?

当第一个线程执行CAS(V,E,U)操作,在获取到当前变量V,准备修改为新值U前,另外两个线程已连续修改了两次变量V的值,使得该值又恢复为旧值,这样我们就无法正确判断这个变量是否已被修改过。

2、ABA问题的解决方案:

AtomicStampedReference:是一个带有时间戳的对象引用,在每次修改后,不仅会设置新值还会记录更改的时间。
AtomicMarkableReference:维护的是一个boolean值的标识,这种方式并不能完全防止ABA问题的发生,只能减少ABA发生的概率。
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1); public static void main(String[] args) throws InterruptedException {
System.out.println("========ABA问题的产生=========");
new Thread(() -> {
atomicReference.compareAndSet(100, 101);
atomicReference.compareAndSet(101, 100); }, "t1").start(); new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100, 2019) + " " + atomicReference.get());
}, "t2").start();
TimeUnit.SECONDS.sleep(2); System.out.println("========ABA问题的解决========="); new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "线程第1次版本号:" + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "线程第2次版本号:" + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "线程第3次版本号:" + atomicStampedReference.getStamp()); }, "t3").start(); new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "线程第1次版本号:" + stamp);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
System.out.println(Thread.currentThread().getName() + "修改成功否:" + result + " 当前最新版本号:" + atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName() + "当前最新值:" + atomicStampedReference.getReference()); }, "t4").start();
}
}

五、原子更新引用

public class AtomicReferenceDemo {

    public static void main(String[] args) {
AtomicReference<User> atomicReference = new AtomicReference<>(); User user = new User("monster", 18);
User updateUser = new User("jack", 25); atomicReference.set(user);
atomicReference.compareAndSet(user, updateUser); System.out.println(atomicReference.get());
}
} @Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
class User {
private String name;
private int age;
}  
  没有英汉互译结果
  请尝试网页搜索

java面试-CAS底层原理的更多相关文章

  1. CAS底层原理与ABA问题

    CAS定义 CAS(Compare And Swap)是一种无锁算法.CAS算法是乐观锁的一种实现.CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B.当预期值A和内存值V相同时,将内存值V修 ...

  2. java - CAS及CAS底层原理

    CAS是什么? CAS的全称为Compare-And-Swap它是一条CPU并发原语,也就是在CPU硬件层面上来说比较并且判断是否设置新值这段操作是原子性的,不会被其他线程所打断.在JAVA并发包ja ...

  3. java面试-synchronized底层实现机制

    一.synchronized的三种应用方式 1.修饰实例方法,锁是当前实例对象,进入同步代码前要获得当前实例的锁 /** * synchronized修饰实例方法,当前线程的锁是实例对象account ...

  4. Java 注解及其底层原理

    目录 什么是注解? 注解的分类 Java自带的标准注解 元注解 @Retention @Documented @Target @Inherited @Repeatable 自定义注解 自定义注解的读取 ...

  5. Java面试& HashMap实现原理分析

    1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端.  数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O( ...

  6. [Java] I/O底层原理之一:字符流、字节流及其源码分析

    关于 I/O 的类可以分为四种: 关于字节的操作:InputStream 和 OutPutStream: 关于字符的操作:Writer 和 Reader: 关于磁盘的操作:File: 关于网络的操作: ...

  7. Java面试之JVM原理总结

    1.什么是JVM? 答:JVM是Java Virual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,他是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟计算机功能来实现 ...

  8. 基于JAVA Socket的底层原理分析及工具实现

    前言 在工作开始之前,我们先来了解一下Socket 所谓Socket,又被称作套接字,它是一个抽象层,简单来说就是存在于不同平台(os)的公共接口.学过网络的同学可以把它理解为基于传输TCP/IP协议 ...

  9. [Java] I/O底层原理之三:NIO

    本篇文章参考自并发编程网 一.NIO 的概述 NIO 由以下几个核心组成 Channels Buffers Selectors 选择器用于监听多个通道的事件(如:链接打开.数据达到),单个线程可以监听 ...

随机推荐

  1. multi selects & mutually exclusive

    multi selects & mutually exclusive 互斥 selects import React, { useState, // useEffect, // useRef, ...

  2. PAUL ADAMS ARCHITECT LTD:拜登上台后,美国房地产走势如何?

    受全球经济危机影响,美国经济的复苏之路并不平坦,然而住宅房地产市场却是少数的例外,表现得非常亮眼.而随着美国拜登总统上台的临近,美国房产市场还会持续当前的发展吗?对于这个问题,近日,美国知名房地产公司 ...

  3. scrapy 运行逻辑

    爬虫的步骤:发送请求获得响应→解析并提取数据→保存数据 我们沿用这个朴素的逻辑去理解scrapy 一.发送请求获得响应 1.爬虫发送请求request到引擎 2.引擎将请求request传递给调度器s ...

  4. 2.go语言入门----变量类型、声明变量、数组、切片

    基本变量类型 介绍几种基本的变量类型:字符串.int.float.bool package main import ( "fmt" ) // 列举几种非常基本的数据类型 func ...

  5. 第39天学习打卡(多线程 Thread Runnable 初始并发问题 Callable )

    多线程详解 01线程简介 Process与Thread 程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念. 进程则是执行程序的一次执行过程,它是一个动态的概念.是系统资源分配的 ...

  6. PAT-1132(Cut Integer )数的拆分+简单题

    Cut Integer PAT-1132 #include<iostream> #include<cstring> #include<string> #includ ...

  7. HDOJ-6681(离散化+线段树)

    Rikka With Cake HDOJ-6681 最终的答案为射线的交点数加一.当然,我们也可以证明.证明需要用到欧拉公式 V−E+F=2 V-E+F=2V−E+F=2 .设射线的交点共 c cc ...

  8. 基于Hi3559AV100 RFCN实现细节解析-(3)系统输入VI分析(HiISP)二 :

    下面随笔系列将对Hi3559AV100 RFCN实现细节进行解析,整个过程涉及到VI.VDEC.VPSS.VGS.VO.NNIE,其中涉及的内容,大家可以参考之前我写的博客: 基于Hi3559AV10 ...

  9. 关于MarkDown语法

    Markdown语法 码云笔记链接:https://gitee.com/out_of_zi_wen/practical-experience/blob/master/Markdown%E8%AF%AD ...

  10. 精确率precession和召回率recall

    假设有两类样本,A类和B类,我们要衡量分类器分类A的能力. 现在将所有样本输入分类器,分类器从中返回了一堆它认为属于A类的样本. 召回率:分类器认为属于A类的样本里,真正是A类的样本数,占样本集中所有 ...