JAVA并发包-AtomicInteger

1.简介

JUC 包提供 了一系列的原子性操作类,这些类都是使用非阻塞算法 CAS 现的 ,相 比使用锁 现原 性操作这在性能上有很大提高。由于原子性操作类的原理都大致相同。

CAS原理:

在Java发展初期,java语言是不能够利用硬件提供的这些便利来提升系统的性能的。而随着java不断的发展,Java本地方法(JNI)的出现,使得java程序越过JVM直接调用本地方法提供了一种便捷的方式,因而java在并发的手段上也多了起来。而在Doug Lea提供的cucurenct包中,CAS理论是它实现整个java包的基石。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前 值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”

通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新 值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。

类似于 CAS 的指令允许算法执行读-修改-写操作,而无需害怕其他线程同时 修改变量,因为如果其他线程修改变量,那么 CAS 会检测它(并失败),算法 可以对该操作重新计算。

CAS失败测试:100线程竞争更新,只会有一个成功;

public class AtomicIntegerTest {

    private static AtomicLong atomicLong = new AtomicLong();

    private final static Integer THREAD_NUM = 100;
    
    public static void main(String[] argv) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);

        for (int i = 0; i < THREAD_NUM; ++i) {
            int finalI = i;
            int finalI1 = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    boolean compareAndSet = atomicLong.compareAndSet(0, finalI1 + 1);
                    System.out.println(compareAndSet);
                    countDownLatch.countDown();
                }
            }).start();
        }
        countDownLatch.await();

        System.out.println(atomicLong.get());
    }
}

2.测试

    两个线程统计两个数组中0的个数,结果为10,没有出现其他异常情况;验证了其线程安全性;

public class AtomicIntegerTest {

    private static AtomicLong atomicLong = new AtomicLong();

    private static Integer[] array1 = new Integer[]{1, 2, 0, 1, 0, 0, 0, 0, 0};

    private static Integer[] array2 = new Integer[]{1, 0, 0, 0, 0, 1, 12, 3, 4};

    private static void incrementValueCountNum(Integer[] array, int targetValue) {
        // System.out.println(Arrays.asList(array1).stream().filter(value->value == 0).count());
        for (int i = 0; i < array.length; ++i) {
            if (array[i].intValue() == targetValue) {
                atomicLong.incrementAndGet();
            }
        }
    }
    public static void main(String[] argv) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);

        new Thread(new Runnable() {
            @Override
            public void run() {
                incrementValueCountNum(array1, 0);
                countDownLatch.countDown();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                incrementValueCountNum(array2, 0);
                countDownLatch.countDown();
            }
        }).start();

        countDownLatch.await();

        System.out.println(atomicLong.get());
    }
}

3.Atmoic-IntegerLong源码解析

      基本属性:

public class AtomicLong extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 1927816293512124184L;
    // 主要依赖unsafe来实现CAS
    // setup to use Unsafe.compareAndSwapLong for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    value字段在AtomicLong内的偏移量
    private static final long valueOffset;

    /**
     * Records whether the underlying JVM supports lockless
     * compareAndSwap for longs. While the Unsafe.compareAndSwapLong
     * method works in either case, some constructions should be
     * handled at Java level to avoid locking user-visible locks.
     */
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

    /**
     * Returns whether underlying JVM supports lockless CompareAndSet
     * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
     */
    private static native boolean VMSupportsCS8();

    static {
        try {
            /// 对valueOffset赋值
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    // 内部维护的值
    private volatile long value;

  public final long incrementAndGet():原子上增加一个当前值;返回增加后的值

public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}

public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        // 自旋更新值,直到成功
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

        return var6
}
public final long decrementAndGet():原子减少一个值:返回递减后的值
 // 同递增方法一样,加-1L
 public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
 }
public final long addAndGet(long delta):原子加delta,返回增加后的值
同递增方法
 public final long addAndGet(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
    }
public final boolean compareAndSet(long expect, long update) :CAS更新值
 // 依赖unsafe类
 public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

getAndAdd,getAndIncreament,getAndDecrment:返回修改之前的数据,但是数据已经修改:

  public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }

    /**
     * Atomically decrements by one the current value.
     *
     * @return the previous value
     */
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }

    /**
     * Atomically adds the given value to the current value.
     *
     * @param delta the value to add
     * @return the previous value
     */
    public final long getAndAdd(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta);
    }

4.总结

  • atmoic包下的原子类,本质都是依靠CAS算法实现的;
  • CAS是用CPU换取上下文开销的一种方式,因此如果CAS自旋时间过长,存在性能问题,因此jdk提出LongAdder解决这个自旋问题,后续会讲;
  •  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章