AtomicLong源码分析与使用(11)

x33g5p2x  于2021-08-23 转载在 Java  
字(6.4k)|赞(0)|评价(0)|浏览(101)

前言

原子变量类可以分为 4 组:

  • 基本类型
    • AtomicBoolean - 布尔类型原子类
    • AtomicInteger - 整型原子类
    • AtomicLong - 长整型原子类
  • 引用类型
    • AtomicReference - 引用类型原子类
    • AtomicMarkableReference - 带有标记位的引用类型原子类
    • AtomicStampedReference - 带有版本号的引用类型原子类
  • 数组类型
    • AtomicIntegerArray - 整形数组原子类
    • AtomicLongArray - 长整型数组原子类
    • AtomicReferenceArray - 引用类型数组原子类
  • 属性更新器类型
    • AtomicIntegerFieldUpdater - 整型字段的原子更新器。
    • AtomicLongFieldUpdater - 长整型字段的原子更新器。
    • AtomicReferenceFieldUpdater - 原子更新引用类型里的字段。

我们今天学习的是AtomicLong 相当于学了基本类型的一组,原理相通,所以不做过多讲解;

一 构造方法与成员函数

源码:

    //  获取Unsafe
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // 偏移
    private static final long valueOffset;

	// jvm 是否支持 Long 类型的CAS 无锁机制
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

	 // jvm 是否支持 Long 类型的CAS 无锁机制
    private static native boolean VMSupportsCS8();

	// 获取实际值的偏移
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    // 实际值
    private volatile long value;

	// 构造方法
    public AtomicLong(long initialValue) {
        value = initialValue;
    }

	// 构造方法
    public AtomicLong() {
    }

从源码角度来说JVM会去判定 JVM是否支持CAS的无锁机制;而核心静态方法也会去获取当前实际值value字段在对象中的偏移地址,这很重要,可以说是CAS核心重点之一计算属性;构造方法支持,无参构造(即整数值为0)还带一个参数的构造;如下两个示例的写法都是正确;

无参构造:

public class AtomicLongTest {

    AtomicLong atomicLong = new AtomicLong();

}

带参构造

public class AtomicLongTest {

    AtomicLong atomicLong = new AtomicLong(2);

}

二 set和get

set 和 get 方法与普通的实体类的set,get没差别;

源码:

public class AtomicLong extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 1927816293512124184L;

	// 获取当前值
    public final long get() {
        return value;
    }

	// 设置新值
    public final void set(long newValue) {
        value = newValue;
    }

}

三 CAS Set 操作

源码:

	// 设置新值,延迟显示
    public final void lazySet(long newValue) {
        unsafe.putOrderedLong(this, valueOffset, newValue);
    }

	 // 设置新值并返回旧值
    public final long getAndSet(long newValue) {
        return unsafe.getAndSetLong(this, valueOffset, newValue);
    }

	 // 当前值是期望值expect 就使用 update 更新当前值;
    public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

	// 当前值是期望值expect 就使用 update 更新当前值;
    public final boolean weakCompareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

CAS 的 set 操作 的本质其实就是更新操作;compareAndSet和 weakCompareAndSet 是典型的直接调用Unsafe的API操作,我们之前文章提到过,关于CAS相关内容不作具体分析;

我们以下列代码为例子,原始值 为2,使用 getAndSet方法将值设置为 6;, 其方法返回结果为2,但实际值已经改为6;

public class AtomicLongTest {

    // 初始值
    AtomicLong atomicLong = new AtomicLong(2);

    public static void main(String[] args) {
        AtomicLongTest atomicLongTest = new AtomicLongTest();
        long result = atomicLongTest.atomicLong.getAndSet(6);
        // 新值:6
        System.out.println("新值:"+atomicLongTest.atomicLong);
        // 原值2
        System.out.println("原值"+result);

    }
}

getAndSetLong 方法内部就是 Unsafe的getAndSetLong方法;

    public final long getAndSetLong(Object obj, long offset, long update) {
        long expect;
        do {
            expect = this.getLongVolatile(obj, offset);
        } while(!this.compareAndSwapLong(obj, offset, expect, update));

        return var6;
    }

四 CAS ADD返回旧值

源码:

	// 当前值 加1 返回旧值
    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }

	// 当前值减一 返回旧值
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }

	// 当前值 加上 偏移值 delta 更新当前值 并返回旧值(当前值)
    public final long getAndAdd(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta);
    }

CAS add操作是指在原始值上直接+1或者-1,然后返回的是原始值;

我们以getAndIncrement 为例子;原始值为2,调用getAndIncrement 方法后,值进行自增为3,但方法的返回值还是原始值2;

public class AtomicLongTest {

    // 初始值
    AtomicLong atomicLong = new AtomicLong(2);

    public static void main(String[] args) {
        AtomicLongTest atomicLongTest = new AtomicLongTest();
        long result = atomicLongTest.atomicLong.getAndIncrement();
        // 新值:3
        System.out.println("新值:"+atomicLongTest.atomicLong);
        // 原值2
        System.out.println("原值"+result);

    }
}

五 CAS ADD 返回新值

源码:

	// 当前值加1并且返回 加1后的新值
    public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
    }

	// 当前值减 1并且返回 减1后的新值
    public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }

	// 当前值加上偏移 delta 并且返回加上偏移后的新值
    public final long addAndGet(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
    }

CAS add操作是指在原始值上直接+1或者-1,然后返回的是新值;

cas add操作返回新值例子如下

无论是调用incrementAndGet 方法 还是 直接打印值,都是新值;原始值就无法看见;

public class AtomicLongTest {

    // 初始值
    AtomicLong atomicLong = new AtomicLong(2);

    public static void main(String[] args) {
        AtomicLongTest atomicLongTest = new AtomicLongTest();
        long result = atomicLongTest.atomicLong.incrementAndGet();
        // 新值:3
        System.out.println("新值:"+atomicLongTest.atomicLong);
        // 新值3
        System.out.println("新值"+result);

    }
}

六 java8新增函数式应用CAS

源码:

	// 将当前值使用 side-effect-free 函数计算后的结果作为 新值 更新,并返回 旧值
    public final long getAndUpdate(LongUnaryOperator updateFunction) {
        long prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsLong(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }

	// 将当前值使用 side-effect-free 函数(java8函数式)计算后的结果作为 新值 更新,并返回 新值
    public final long updateAndGet(LongUnaryOperator updateFunction) {
        long prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsLong(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }

	 // 将当前值与x使用 side-effect-free 函数(java8函数式)计算后的结果作为新值更新,返回旧值;
    public final long getAndAccumulate(long x,
                                       LongBinaryOperator accumulatorFunction) {
        long prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsLong(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }

	// 将当前值与x使用 side-effect-free 函数(java8函数式)计算后的结果作为新值更新,返回新值;
    public final long accumulateAndGet(long x,
                                       LongBinaryOperator accumulatorFunction) {
        long prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsLong(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }

java 8 函数式编程知识追寻者举个简单的例子如下,原始值2 更新值为4;

public class AtomicLongTest {

    // 初始值
    AtomicLong atomicLong = new AtomicLong(2);

    public static void main(String[] args) {
        AtomicLongTest atomicLongTest = new AtomicLongTest();
        long result = atomicLongTest.atomicLong.getAndUpdate((val) -> {
            return val+2;
        });
        // 新值:3
        System.out.println("新值:"+atomicLongTest.atomicLong);
        // 旧值2
        System.out.println("旧值"+result);

    }
}

参数其实是 函数LongUnaryOperator , 其内部 有一个方法applyAsLong(long operand); 返回值为 long ;

具体的怎么操作实现看个人发挥;

七 值类型转换

源码:

	// 返回 string代表的当前值
    public String toString() {
        return Long.toString(get());
    }

	// 强制转换 ,返回 int 类型值
    public int intValue() {
        return (int)get();
    }

	// 返回值
    public long longValue() {
        return get();
    }

	// 强制转换 ,返回 float 类型值
    public float floatValue() {
        return (float)get();
    }

	 // 强制转换 ,返回 double 类型值
    public double doubleValue() {
        return (double)get();
    }

八 多线程中使用 AtomicLong

多线程中使用AtomicLong 其实非常简单,示例如下,使用AtomicLong 提供的API就可以达到原子性操作;

 private static AtomicLong atomicLong = new AtomicLong();

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            for (int i = 0; i < 10000; i++) {
                atomicLong.getAndIncrement();
            }
        };
        // 启动2 个线程
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.start();
        t2.start();
        // 携程
        t1.join();
        t2.join();
        //atomicLong=20000
        System.out.println("atomicLong=" + atomicLong);
    }

相关文章