2019年互联网(1.2)
一.JUC多线程及并发包
1.2.CAS你知道吗
CAS(Compare And Swap):
比较并且交换
比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值
先检查后操作模式发生在代码中首先检查一个变量的值,然后再基于这个值做一些操作
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。
CAS,一种乐观锁机制,如果对象当前值和期望值一样,那么则将对象的值设置成新值。和悲观锁不一样,它不需要抢占锁,它是一种尝试性的,能有效地提高效率,它的全称是 compareAndSwap ,依赖于硬件的原子操作实现。
package com.ybzn._01.juc;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Hugo
* @time 2021/2/11
*
* CAS(Compare And Set ): **比较并且交换** initialValue
* 更新值与期望值(update and expect)
*/
public class CASDemo_1_2 {
public static void main (String[] args) {
AtomicInteger atomicInteger =new AtomicInteger(5);//期望值设置为5
boolean b;
b = atomicInteger.compareAndSet(5, 2019);
System.out.println(b+ " current date"+atomicInteger.get());
boolean b1 = atomicInteger.compareAndSet(5, 2019);
System.out.println(b1+ " current date"+atomicInteger.get());
}
}
结果分析: 当期望值为5的时候,将5设置为2019, 先比较后设置,然后在此比较,则会发现比较失败了!

CAS底层原理?如果知道,谈谈对Unsafe的理解
其中 unsafe.class是rt.jar(java运行包)里面自带的类,里面的方法都是Nation,
Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题,直接操作内存空间地址的方法来实现的,不需要寻找变量
1.UnSafe 是CAS的核心类 由于Java 方法无法直接访问底层 ,需要通过本地(native)方法来访问,UnSafe相当于一个后门,基于该类可以直接操作特额定的内存数据【通过操作内存地址来实现的】.UnSafe类在于sun.misc包中,其内部方法操作可以向C的指针一样直接操作内存,因为Java中CAS操作的助兴依赖于UNSafe类的方法.注意UnSafe类中所有的方法都是native修饰的,也就是说UnSafe类中的方法都是直接调用操作底层资源执行响应的任务2.变量ValueOffset,便是该变量在内存中的偏移地址,因为UnSafe就是根据内存偏移地址获取数据的
3.变量value和volatile修饰,保证了多线程之间的可见性.
源码分析:unsafe.class类
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
↓
↓
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;
}
假设线程A和线程B两个线程同时执行getAndAddInt操作(分别在不同的CPU上):
1.AtomicInteger里面的value原始值为3,即主内存中AtomicInteger的value为3,==根据JMM模型==,线程A和线程B各自持有一份值为3的value的副本分别到各自的工作内存.
2.线程A通过getIntVolatile(var1,var2) 拿到value值3,这是线程A被挂起.
3.线程B也通过getIntVolatile(var1,var2) 拿到value值3,此时刚好线程B没有被挂起并执行compareAndSwapInt方法比较内存中的值也是3 成功修改内存的值为4 线程B打完收工 一切OK.
4.这是线程A恢复,执行compareAndSwapInt方法比较,发现自己手里的数值和内存中的数字4不一致,说明该值已经被其他线程抢先一步修改了,那A线程修改失败,只能重新来一遍了.
5.线程A重新获取value值,因为变量value是volatile修饰,所以其他线程对他的修改,线程A总是能够看到,线程A继续执行compareAndSwapInt方法进行比较替换,直到成功.
CAS缺点
- 循环时间长开销很大(使用乐观锁机制,do{}while()一直会循环等待,浪费CPU)
- 只能保证一个共享变量的原子性
- ABA问题

浙公网安备 33010602011771号