【JUC并发编程】CAS原子类底层原理&&源码分析&&手写AtomicInteger&&手写Lock锁

一、什么是CAS

CAS: Compare and Swap,翻译成比较并交换。 执行函数CAS(V,E,N)
CAS有3个操作数,内存值V,旧的预期值E,要修改的新值N。当且仅当预期值E和内存值V相同时,将内存值V修改为N,否则什么都不做。
保证线程安全性的问题 原子性

V: 全局共享变量 原子类中 value值 ===0
E: 旧的预期值
N: 修改的新值
CAS:无锁----自旋控制乐观锁
缺点:自旋 会消耗的cpu的资源 cas 死循环 空转问题 cpu飙高
底层基于unsafe类实现
在这里插入图片描述
This === atomicInteger 对象;
Valueoffset == 值偏移量;

// paramObject=atomicInteger 对象
// Valueoffset ==值偏移量;
// paramInt=1
public final int getAndAddInt(Object paramObject, long paramLong, int paramInt) {
	// 循环目的 就是为了cas的重试
    while (true) {
	// paramObject=atomicInteger 对象
	// Valueoffset == 值偏移量;
	// 获取当前的atomicInteger  中 value值
        int i = getIntVolatile(paramObject, paramLong);==i==000
        if (compareAndSwapInt(paramObject, paramLong, i, i + paramInt))
            return i;
    }
}

二、同步之原子类(Atomic类)

Java并发包类库
原子类 cas

比如我们所学习的:原子类同步之原子类(Atomic类)

1. 原子更新基本类型类 cas

AtomicBoolean: 原子更新布尔类型。
AtomicInteger: 原子更新整型。
AtomicLong: 原子更新长整型。

2. 原子更新数组

AtomicIntegerArray: 原子更新整型数组里的元素。
AtomicLongArray: 原子更新长整型数组里的元素。
AtomicReferenceArray: 原子更新引用类型数组里的元素。

3. 原子更新引用类型

AtomicReference: 原子更新引用类型。
AtomicReferenceFieldUpdater: 原子更新引用类型的字段。
AtomicMarkableReferce: 原子更新带有标记位的引用类型,可以使用构造方法更新一个布尔类型的标记位和引用类型。

4. 原子更新字段类

AtomicIntegerFieldUpdater: 原子更新整型的字段的更新器。
AtomicLongFieldUpdater: 原子更新长整型字段的更新器。
AtomicStampedFieldUpdater: 原子更新带有版本号的引用类型。

5. JDK8新增原子类

DoubleAccumulator
LongAccumulator
DoubleAdder
LongAdder

Java 层面 Unsafe实现

三、使用atomicInteger计数

atomicInteger 计数能够保证线程安全问题。

public class Thread01 extends Thread {
    //    private static int sum = 0;
    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    @Override
    public void run() {
        sum();
    }

    public void sum() {
        for (int i = 0; i < 10000; i++) {
//            sum++;
            atomicInteger.incrementAndGet();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread01 t1 = new Thread01();
        Thread01 t2 = new Thread01();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("sum:" + atomicInteger.get());
    }
}

四、使用atomicInteger底层原理

atomicInteger底层基于CAS实现

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

Unsafe类,全限定名是sun.misc.Unsafe,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。
Unsafe类是"final"的,不允许继承。且构造函数是private的

传递三个参数 【this(atomicInteger this) valueOffset(成员变量在内存中偏移量) ,1 】+1

Value=对象内存地址+内存偏移量
CAS前置知识Unsafe
Unsafe对象提供了非常底层的,操作内存、线程的方法,Unsafe 对象不能直接调用,只能通过反射获得 java9已经移除Unsafe

Cas底层 Unsafe类实现-----java层面
Cas 能够保证线程安全性 数据的原子性------cpu硬件支撑

private static AtomicInteger atomicInteger = new AtomicInteger();

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    // 1.使用反射获取theUnsafe属性
    Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    // 2.获取到unsafe
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);
    // 1.获取域的偏移地址
    long idOffset = unsafe.objectFieldOffset(Lock.class.getDeclaredField("state"));
    Lock lock = new Lock();
    boolean result = unsafe.compareAndSwapInt(lock, idOffset, 0, 1);
    System.out.println((ClassLayout.parseInstance(lock).toPrintable()));
    System.out.println(result);
}

static class Lock {
    int state;
}
public final int getAndAddInt(Object paramObject, long paramLong, int paramInt) {
    while (true) {
        int i = getIntVolatile(paramObject, paramLong);
        if (compareAndSwapInt(paramObject, paramLong, i, i + paramInt))
            return i;
    }
}

从对象的内存处开始,获得原始字节偏移量,用于存储实力对象的内存
在这里偏移量的意思就像我们 new 一个数组,数组的地址就是数组地一个元素的地址,假如数组地址是 a,第二个元素就是a+1,其中+1就是偏移量。对应的对象的一个属性的偏移量就是其对象的地址开始增加,增加的数就是这个filed的偏移量。
对于VALUE这个值我们知道了,他是AtomicInteger中的value属性对应的偏移量,就是对象地址+VALUE = value的地址

循环:获取当前atomicInteger 原子类的 value=0 i=0
compareAndSwapInt(1,1,1+1);
在这里插入图片描述
AtomicInteger 底层如何实现
1.创建了一个AtomicInteger 类
2.先定义一个 int类型value值
3.底层都是基于unsafe类实现----

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// 参数1 this new AtomicInteger 
// 参数2  valueOffset
// 参数3  1

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do { 
// var5 
        var5 = this.getIntVolatile(var1, var2);
//this.compareAndSwapInt(var1, var2, var5, var5 + var4)
// var1 this new AtomicInteger 对象
// var2 value的值 offset
// var5---当前value值=0 (旧预期值 E)
// var5(0) + var4(1)---当前value值+1
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

五、compareAndSet原理分析

compareAndSet(e,n);
E==V 则将N的值赋值给V;

1. 手写AtomicInteger

public class DemoAtomicInteger {
    // 设定v=0
    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    public void incrementAndGet() {
//        Integer v = atomicInteger.get();
        Integer e = atomicInteger.get();
        // 一直重试不断循环 如果cas 失败的 则一直不断重试
        for (; ; ) {
            if (atomicInteger.compareAndSet(e, e + 1)) {
                return;
            }
        }
    }

    public static void main(String[] args) {
        DemoAtomicInteger demoAtomicInteger = new DemoAtomicInteger();
        demoAtomicInteger.incrementAndGet();
        demoAtomicInteger.incrementAndGet();
        demoAtomicInteger.incrementAndGet();
        System.out.println(atomicInteger.get());
    }
}

2. 手写Lock锁

Aqs+cas实现
默认锁的状态是为=0 如果获取到锁 则锁的状态就是为1

public class DemoLock {
    /**
     * 默认锁的状态是为=0  如果获取到锁  则锁的状态就是为1
     */
    private AtomicInteger lockState = new AtomicInteger(0);
    private Thread cuLockThread;

    /**
     * 获取锁
     */
    public void lock() {
        for (; ; ) {
            if (lockState.compareAndSet(0, 1)) {
                return;
            }
        }
    }

    /**
     * 释放锁
     */
    public void unLock() {
        for (; ; ) {
            if (lockState.compareAndSet(1, 0)) {
                cuLockThread = null;
                return;
            }

        }

    }

    public static void main(String[] args) {
        DemoLock demoLock = new DemoLock();
        System.out.println("3");
    }
}

3. CAS aba的问题

Cas主要检查 内存值V与旧的预值值=E是否一致,如果一致的情况下,则修改。
这时候会存在ABA的问题:
如果将原来的值A,改为了B,B有改为了A 发现没有发生变化,实际上已经发生了变化,所以存在Aba问题。
解决办法:通过版本号码,对每个变量更新的版本号码做+1

解决aba问题是否大:概念产生冲突,但是不影响结果,换一种方式 通过版本号码方式。
在这里插入图片描述

4. AtomicMarkableReference

(1)第一个参数expectedReference:表示预期值。
(2)第二个参数newReference:表示要更新的值。
(3)第三个参数expectedStamp:表示预期的时间戳。
(4)第四个参数newStamp:表示要更新的时间戳。

public class Test004 {
    // 注意:如果引用类型是Long、Integer、Short、Byte、Character一定一定要注意值的缓存区间!
    // 比如Long、Integer、Short、Byte缓存区间是在-128~127,会直接存在常量池中,而不在这个区间内对象的值则会每次都new一个对象,那么即使两个对象的值相同,CAS方法都会返回false
    // 先声明初始值,修改后的值和临时的值是为了保证使用CAS方法不会因为对象不一样而返回false
    private static final Integer INIT_NUM = 1000;
    private static final Integer UPDATE_NUM = 100;
    private static final Integer TEM_NUM = 200;
    private static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(INIT_NUM, 1);

    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                Integer value = (Integer) atomicStampedReference.getReference();
                int stamp = atomicStampedReference.getStamp();
                System.out.println(Thread.currentThread().getName() + " : 当前值为:" + value + " 版本号为:" + stamp);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // value 旧值 内存中的值   UPDATE_NUM 修改的值
                if (atomicStampedReference.compareAndSet(value, UPDATE_NUM, 1, stamp + 1)) {
                    System.out.println(Thread.currentThread().getName() + " : 当前值为:" + atomicStampedReference.getReference() + " 版本号为:" + atomicStampedReference.getStamp());
                } else {
                    System.out.println("版本号不同,更新失败!");
                }
            }
        }, "线程A").start();

    }
}

六、CAS 优点

CAS 优点: 避免用户态到内核态切换
缺点: 非常消耗cpu的资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超级码里喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值