【十六】Java多线程J.U.C之atomic包(原子性、CAS、UnSafe)

版权声明:转载注明出处 https://blog.csdn.net/jy02268879/article/details/86073671

一、概述

原子性提供了互斥访问,同一时刻只会有一个线程对资源进行操作。

该包下提供了具有原子性的数据类型。它是通过CAS来实现原子性的。

java.util.concurrent.atomic

以AtomicInteger的incrementAndGet()方法为例

    /**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

它使用了Unsafe类的getAndAddInt方法,追踪到该方法查看源码

    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;
    }

 里面的compareAndSwap方法简称就是CAS

Unsafe类简介

native表示不是由Java语言实现的,是由其他语言实现的

主要作用

1.直接操作内存

    //分配内存
    public native long allocateMemory(long var1);

    //重新分配内存
    public native long reallocateMemory(long var1, long var3);

    public native void setMemory(Object var1, long var2, long var4, byte var6);

    public void setMemory(long var1, long var3, byte var5) {
        this.setMemory((Object)null, var1, var3, var5);
    }

    public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);

    public void copyMemory(long var1, long var3, long var5) {
        this.copyMemory((Object)null, var1, (Object)null, var3, var5);
    }

    //释放内存
    public native void freeMemory(long var1);

2.字段的定位与修改

3.线程的挂起与恢复

扫描二维码关注公众号,回复: 5117421 查看本文章

LockSupport调用的最底层就是UnSafe里面的unpark park 

    public native void unpark(Object var1);

    public native void park(boolean var1, long var2);

4.CAS操作(乐观锁)

    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

二、CAS原理(Compare And Swap)

CAS指令的三个操作数,当前内存地址存放的实际值,预期值,更新的新值。
当且仅当预期值和当前内存地址存放的实际值相同时,将内存值修改为新值,否则重试,直到成功为止

上面AtomicInteger(原子更新基本类型都与此雷同、原子更新数组类型比如AtomicIntegerArray也基本相同,基本一致,只不过在AtomicIntegerArray的方法中会多一个指定数组索引位i)追踪的代码用伪代码表示为:

boolean flag = false;

do{

    获取当前内存地址中实际值

    if (实际值==预期值){

        往内存地址中写入新值

        flag = true (结束循环)

    }

}while(!flag)

优点:

1.非阻塞算法

2.原子操作成本低

缺点:

1.ABA问题。一个旧值A变为了成B,然后再变成A,在做CAS时检查发现旧值为A,但是实际上却是发生了变化。解决方案可以用乐观锁方式,添加一个版本号。原来的变化路径A->B->A就变成了1A->2B->3C。

例如:AtomicStampedReference原子更新引用类型,这种更新方式会带有版本号,解决CAS的ABA问题。

2.自旋时间太长。CAS如果长时间不成功,会给CPU带来非常大的执行开销。

3.只能保证一个共享变量的原子操作。JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。

猜你喜欢

转载自blog.csdn.net/jy02268879/article/details/86073671