java面试-CAS底层原理
一、CAS是什么?
比较并交换,它是一条CPU并发原语。
CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说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问题的解决方案:
- 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底层原理的更多相关文章
- CAS底层原理与ABA问题
CAS定义 CAS(Compare And Swap)是一种无锁算法.CAS算法是乐观锁的一种实现.CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B.当预期值A和内存值V相同时,将内存值V修 ...
- java - CAS及CAS底层原理
CAS是什么? CAS的全称为Compare-And-Swap它是一条CPU并发原语,也就是在CPU硬件层面上来说比较并且判断是否设置新值这段操作是原子性的,不会被其他线程所打断.在JAVA并发包ja ...
- java面试-synchronized底层实现机制
一.synchronized的三种应用方式 1.修饰实例方法,锁是当前实例对象,进入同步代码前要获得当前实例的锁 /** * synchronized修饰实例方法,当前线程的锁是实例对象account ...
- Java 注解及其底层原理
目录 什么是注解? 注解的分类 Java自带的标准注解 元注解 @Retention @Documented @Target @Inherited @Repeatable 自定义注解 自定义注解的读取 ...
- Java面试& HashMap实现原理分析
1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端. 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O( ...
- [Java] I/O底层原理之一:字符流、字节流及其源码分析
关于 I/O 的类可以分为四种: 关于字节的操作:InputStream 和 OutPutStream: 关于字符的操作:Writer 和 Reader: 关于磁盘的操作:File: 关于网络的操作: ...
- Java面试之JVM原理总结
1.什么是JVM? 答:JVM是Java Virual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,他是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟计算机功能来实现 ...
- 基于JAVA Socket的底层原理分析及工具实现
前言 在工作开始之前,我们先来了解一下Socket 所谓Socket,又被称作套接字,它是一个抽象层,简单来说就是存在于不同平台(os)的公共接口.学过网络的同学可以把它理解为基于传输TCP/IP协议 ...
- [Java] I/O底层原理之三:NIO
本篇文章参考自并发编程网 一.NIO 的概述 NIO 由以下几个核心组成 Channels Buffers Selectors 选择器用于监听多个通道的事件(如:链接打开.数据达到),单个线程可以监听 ...
随机推荐
- flutter sqlite持久化数据
dependencies: path: sqflite: sqflite_common_ffi: import 'dart:io'; import 'package:flutter/material. ...
- 我眼中的价值币——NGK(下)
跨链交互方案并不是区块链世界中的一个新课题.自比特币诞生揭开智能合约的序幕之后,跨链交互的需求便产生了.但是,经过十年的发展,市场中的跨链解决方案进展缓慢,究之原因有以下几个方面. 首先,区块链的去中 ...
- DeFi里的灰度?每月获得高收益?BGV代币初探
2020年已经接近了尾声,但是DeFi市场的热闹场面并没有停止,或者说,一直在延续.资本市场不断将大批的资金投入到DeFi市场中,以求在这波热潮中赚得一波又一波红利. 美国时间12月21日,Bacca ...
- 「NGK每日快讯」12.18日NGK公链第45期官方快讯!
- [转]SIFT,SURF,ORB,FAST 特征提取算法比较
转载地址:https://blog.csdn.net/vonzhoufz/article/details/46461849 主要的特征检测方法有以下几种,在一般的图像处理库中(如opencv, VLF ...
- 06_MySQL数据类型
MySQL数据类型
- 【Notes_2】现代图形学入门——向量与线性代数
向量与线性代数 点乘和叉乘 Dot Multiplication 点乘在图形学的应用 (1) 求两个向量之间的夹角: $$\cos(\theta) = \frac{(\vec{a} \cdot \ve ...
- 微信小程序:页面生命周期
小程序生命周期分为应用生命周期和页面生命周期 1.Onload:页面加载时触发,一般在onLoad中发送异步请求来初始化页面数据. 2.onShow:页面显示时触发 3.onReady:页面初次渲染完 ...
- 小白养成记——Java比较器Comparable和Comparator
一.使用情景 1. 调用Arrays.sort()方法或Collections.sort()方法对自定义类的对象排序 以Arrays.sort()为例.假定有如下自定义的Person类 1 publ ...
- DRF 三大认证之身份认证
目录 路由组件补充 三大认证 一.身份认证 1.如何进行身份认证 2.jwt认证规则原理 3.jwt的组成 4.jwt的使用方法 4.1 签发算法 4.2 校验算法 4.3 刷新算法 二.权限认证 三 ...