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, 先比较后设置,然后在此比较,则会发现比较失败了!

image-20210211020705056

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问题
posted @ 2021-02-14 23:26  Hugo_nice  阅读(67)  评论(0)    收藏  举报