JUC(二)知识点学习

每次到周五周六就比较懒,不想写,但是看下某些用工写文章到一点我就继续拿起笔,开始搞。

一 CAS

什么是**CAS**,中文翻译叫比较并且交换。CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个**原子**操作。
![cas](https://img-blog.csdnimg.cn/20190803112359617.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ppZmVuX2JsYWNr,size_16,color_FFFFFF,t_70)

首先实例化赋值为10,将10和2019比较,显然2019>10,比较后将值更新为2019.第二次与2018比较显然2019>2018,因此false。

二 CAS底层原理,谈谈unsafe。

我们看下AtomicInteger这个类
在这里插入图片描述
实现了number接口,并且取值的时候
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
调用的方法,用unsafe,而不是直接操作值,那这个unsafe有啥神奇的地方呢。
1.UnSafe 是CAS的核心类 由于Java 方法无法直接访问底层 ,需要通过本地(native)方法来访问,UnSafe相当于一个后面,基于该类可以直接操作特额定的内存数据.UnSafe类在于sun.misc包中,其内部方法操作可以向C的指针一样直接操作内存,因为Java中CAS操作的助兴依赖于UNSafe类的方法.
注意UnSafe类中所有的方法都是native修饰的,也就是说UnSafe类中的方法都是直接调用操作底层资源执行响应的任务
2.变量ValueOffset,便是该变量在内存中的偏移地址,因为UnSafe就是根据内存偏移地址获取数据的
这个说法比较官方,通俗点就是相当于我们平时部署项目时,不会直接访问服务器,一般是通过跳板机再去访问,我们不能直接远程操作,但是跳板机可以远程操作服务器。

CAS是什么,我们来深入一点。
CAS的全称为Compare-And-Swap ,它是一条CPU并发原语.它的功能是判断内存某个位置的值是否为预期值,如果是则更新为新的值,这个过程是原子的.
CAS并发原语提现在Java语言中就是sun.miscUnSaffe类中的各个方法.调用UnSafe类中的CAS方法,JVM会帮我实现CAS汇编指令.这是一种完全依赖于硬件 功能,通过它实现了原子操作,再次强调,由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许中断,也即是说CAS是一条原子指令,不会造成所谓的数据不一致的问题.

三 CAS缺点

cas保证了操作的原子性,解决了许多并发下的问题,但是特也有缺点。
1.循环时间长开销大
在这里插入图片描述
2.只能保证一个共享变量的原子性
对于一个共享变量我们可使用cas让他执行的操作具备原子性,但是在多个共享变量的情况下cas就显得无能为力,我们需要通过加锁的机制让这些共享变量保证原子性。
3.ABA问题
线程1 从内存当中获取到变量V的值是A,线程2也从内存当中获取到V的值A,然后线程2将V的值修改成B, 然后线程2又将变量V的值改成了A,这时候线程1 判断变量V的值也是A,然后就进行修改成功了。

这个时候可能就会有个疑问即使我中间变化了,可我最终还是把值给改成我所期待的值,并不会有影响,这种想法只是在修改一个Integer类型时,可能中间修改过多少次并没有太大的影响,但是如果是链表的话,那么这个影响是很大的

比如单向链表,A–>B—>null, 如图


加入此时线程1 想要把栈顶A 替换为B, head.comareAndSet(A,B),还没有执行成功
此时线程2 进入,将链表当中A后面的元素成功设置成了C和D,

在这里插入图片描述

此时线程1 回来执行compareAndSwap操作,发现栈顶还是A ,然后执行成功,但是此时链表变成了如下
在这里插入图片描述参考

三 原子类automicinterger的ABA问题

在这里插入图片描述
其实上面的图已经很好解释了ABA问题的产生原因,因为存在一个时间差。
原子引用
三种原子更新基本类型分别是:
AtomicBoolean:原子更新布尔类型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新长整型。
他们的方法有如下这些,下面给出AtomicInteger的,其他类似。
public final int getAndSet(int newValue) //给AtomicInteger设置newValue并返回加oldValue
public final boolean compareAndSet(int expect, int update) //如果输入的值和期望值相等就set并返回true/false
public final int getAndIncrement() //对AtomicInteger原子的加1并返回当前自增前的value
public final int getAndDecrement() //对AtomicInteger原子的减1并返回自减之前的的value
public final int getAndAdd(int delta) //对AtomicInteger原子的加上delta值并返加之前的value
public final int incrementAndGet() //对AtomicInteger原子的加1并返回加1后的值
public final int decrementAndGet() //对AtomicInteger原子的减1并返回减1后的值
public final int addAndGet(int delta) //给AtomicInteger原子的加上指定的delta值并返回加后的值
我们来用代码看下在这里插入图片描述
这里我们第一次操作zs和期望值zs相同,显然因为值相同会进行更新赋值操作,操作之前是zs,之后就变成了ls.当我们进行第二次操作时,ls和期望值zs不一样,因此false.
那看下ABA问题的产生和解决办法
在这里插入图片描述
在这里插入图片描述
两次结果的不同就在于我们加了时间戳。

public final boolean compareAndSet(intexpect, int update) {
   return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
private static final Unsafe unsafe = Unsafe.getUnsafe();
  1. compareAndSet 方法首先判断当前内存值this是否等于预期值current;
  2. 如果当前值 = current,说明 AtomicInteger 类的值没有被其他线程修改,则将内存值更新为next
  3. 如果当前值 != current,说明 AtomicInteger 类的值已经被其他类修改了,这时会再次进入循环重新获取更新后值并比较。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值