Java并发包原子操作类

一、原子变量操作类以AtomicLong为例

AtomicLong是原子性递增或者递减类,基本上所有的方法都是对unsafe实例的方法的封装使用,用户不能对unsafe直接使用,原子操作类封装后可直接供用户使用。

1.初始化及变量

    // setup to use Unsafe.compareAndSwapLong for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();//引用unsafe单例
    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();//判断JVM是否支持Long类型无锁CAS

    /**
     * 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 = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));//获取AtomicLong中变量value的偏移量,以便unsafe操作
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile long value;//实际变量值

    /**
     * Creates a new AtomicLong with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicLong(long initialValue) {
        value = initialValue;
    }

    /**
     * Creates a new AtomicLong with initial value {@code 0}.
     */
    public AtomicLong() {
    }

2.部分方法:基本上所有的方法都是对unsafe的方法的封装使用

    /**
     * Atomically increments by one the current value.
     * unsafe 自增返回自增值
     * @return the updated value
     */
    public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
    }

    /**
     * Atomically decrements by one the current value.
     * unsafe 自减返回自减值
     * @return the updated value
     */
    public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }

    /**
     * Atomically increments by one the current value.
     * unsafe 获取当前值,后自增
     * @return the previous value
     */
    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }

    /**
     * Atomically decrements by one the current value.
     * unsafe 获取当前值,后自减
     * @return the previous value
     */
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }

    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     * 赋值的原子操作
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

3、实现

public class AtomicTest {

    private static AtomicLong atomicLong = new AtomicLong();

    private static Integer[] arrayOne = new Integer[]{0,1,2,3,0,5,6,0,56,0};

    private static Integer[] arrayTwo = new Integer[]{10,1,2,3,0,5,6,0,56,0};

    private static void zeroCount(Integer[] array){
        if (array == null || array.length == 0){
            return;
        }
        int size = array.length;
        for (int i = 0; i< size; ++i){
            if (array[i].intValue() == 0){
                atomicLong.incrementAndGet();//计算0的个数
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                zeroCount(arrayOne);
            }
        });
        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                zeroCount(arrayTwo);
            }
        });

        threadOne.start();
        threadTwo.start();
        threadOne.join();
        threadTwo.join();
        System.out.println("zero count :" + atomicLong.get());

    }
}

在没有原子类的情况下,实现计数器需要使用一定的同步措施,比如使用synchronized关键字等,属于阻塞同步,线程切换会导致核心态与用户态切换,对性能有一定的损耗,

而AtromicLong使用的是CAS非阻塞算法,一直运行在用户态,性能更好。

但在高并发情况下使用AtomicLong,会导致大量线程竞争更新同一个原子变量,其中只有一个线程CAS操作会成功,其他大量线程竞争失败后,会不断地循环进行自旋尝试,白白浪费CPU资源。

于是JDK8新增一个LongAdder来克服AtomicLong的缺点。把一个value分解成多个变量Cell,让同样多的线程去竞争多个Cell资源(多对一关系转变为多对多关系),提升性能;

代码中的多个变量实际是一个Cells数组,由于数组内存连续,容易产生伪共享,需要@sun.misc.Contended注解修饰Cell避免伪共享。

 二、LongAdder类

1. LongAdder的父类Striped64

1)声明了三个变量:一个延迟初始化的原子性更新数组Cells、一个基值变量base和一个用来实现自旋锁的变量cellsBusy。

    /**
     * Table of cells. When non-null, size is a power of 2.Cell数组,不为空是,大小是2的n次方
     */
    transient volatile Cell[] cells;

    /**
     * Base value, used mainly when there is no contention, but also as
     * a fallback during table initialization races. Updated via CAS.
     */
    transient volatile long base;//一个保险措施:相当于AtomicLong的value,在单线程时使用;或者多线程并发时cells数组初始化失败,作为value用回原AtomicLong逻辑

    /**
     * Spinlock (locked via CAS) used when resizing and/or creating Cells.调整Cells大小或者创建Cells时自旋锁
     */
    transient volatile int cellsBusy;

2)初始化了unsafe实例引用(单例),注意这里多获取了Thread类threadLocalRandomProbe的偏移量

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long BASE;
    private static final long CELLSBUSY;
    private static final long PROBE;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> sk = Striped64.class;
            BASE = UNSAFE.objectFieldOffset
                (sk.getDeclaredField("base"));
            CELLSBUSY = UNSAFE.objectFieldOffset
                (sk.getDeclaredField("cellsBusy"));
            Class<?> tk = Thread.class;
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

3)定义了Cell静态内部类,Cell初始化了unsafe引用,提供CAS操作保证了Cell中value值的原子性,另外@sun.misc.Contended修饰避免了伪共享。

    /**
     * Padded variant of AtomicLong supporting only raw accesses plus CAS.
     *
     * JVM intrinsics note: It would be possible to use a release-only
     * form of CAS here, if it were provided.
     */
    @sun.misc.Contended static final class Cell {
        volatile long value;
        Cell(long x) { value = x; }
        final boolean cas(long cmp, long val) {
            return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
        }

        // Unsafe mechanics
        private static final sun.misc.Unsafe UNSAFE;
        private static final long valueOffset;
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> ak = Cell.class;
                valueOffset = UNSAFE.objectFieldOffset
                    (ak.getDeclaredField("value"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

2.LongAdder的方法

long sum():返回计算器的值,内部操作是累加所有Cell的value,但是求和时未加锁,返回结果不是一个原子快照;不并发时结果准确,并发时结果可能不准确了

void reset():重置操作,base置0,cells数组中元素置0;

long sumThenReset:返回sun后将cells,base重置为0;

T TValue():返回对应基本类型强定义装换(T)sum();

void add(long x):

    /**
     * Adds the given value.
     *
     * @param x the value to add
     */
    public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
        if ((as = cells) != null || !casBase(b = base, b + x)) {//cells不为空或者base累加CAS操作失败
            boolean uncontended = true;//
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))//cells为空 或 cells大小为0 或 映射的cell为空 或 cell存在但cas操作失败时
                longAccumulate(x, null, uncontended);//包含cells数组被初始化和扩容的逻辑代码
        }
    }

void longAccumulate(long x,LongBinaryOperator fn,boolean wasUncontended):是父类Striped64的方法,LongAdder继承了此方法并没有重写。它主要处理cells数组初始化、创建、扩容及多线程争用同一cell的问题

    /**
     * Handles cases of updates involving initialization, resizing,
     * creating new Cells, and/or contention. See above for
     * explanation. This method suffers the usual non-modularity
     * problems of optimistic retry code, relying on rechecked sets of
     * reads.
     * 处理涉及cells初始化,扩容,创建及争用的情况,该方法会遇到乐观重试代码的模块性问题,依赖与重新检查的读取
     * @param x the value
     * @param fn the update function, or null for add (this convention
     * avoids the need for an extra field or function in LongAdder).
     * @param wasUncontended false if CAS failed before call
     */
    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        int h;
     //初始化当前线程threadLocalRandomProbe的值
        if ((h = getProbe()) == 0) {
            ThreadLocalRandom.current(); // force initialization
            h = getProbe();
            wasUncontended = true;
        }
        boolean collide = false;                // True if last slot nonempty
        for (;;) {
            Cell[] as; Cell a; int n; long v;
            //cells不为空时扩容
            if ((as = cells) != null && (n = as.length) > 0) {
                //哈希函数除余法:probe%(length-1)得到当前线程对应cells数组中的Cell
                //cell为空
                if ((a = as[(n - 1) & h]) == null) {
                    if (cellsBusy == 0) {       // Try to attach new Cell
                        Cell r = new Cell(x);   // Optimistically create
                        //cellsBusy的CAS配合上面for循环实现自旋锁,乐观重试
                        if (cellsBusy == 0 && casCellsBusy()) {
                            boolean created = false;
                            try {               // Recheck under lock
                                Cell[] rs; int m, j;
                                //再次校验cell为空,后赋值,重新检查
                                if ((rs = cells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {
                                    rs[j] = r;
                                    created = true;
                                }
                            } finally {
                                cellsBusy = 0;
                            }
                            if (created)
                                break;
                            continue;           // Slot is now non-empty
                        }
                    }
                    collide = false;
                }
                //CAS操作失败时 下面方法advanceProbe(probe) rehash probe的值后重新循环一遍
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                //当前Cell存在,则执行CAS设置add后终止
                else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                             fn.applyAsLong(v, x))))
                    break;
                //当前Cell数组元素个数大于CPU个数,多线程访问了同一个cell导致cells != as
                else if (n >= NCPU || cells != as)
                    collide = false;            // At max size or stale
                //是否有冲突
                else if (!collide)
                    collide = true;
                //如果当前元素个数没有达到CPU个数并且多线程访问同一个cell则扩容
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        if (cells == as) {      // Expand table unless stale
                            //扩容策略*2
                            Cell[] rs = new Cell[n << 1];
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            cells = rs;
                        }
                    } finally {
                        cellsBusy = 0;
                    }
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                //为了能够找到一个空闲的Cell,重新计算h的值(rehash),xorshift算法生成随机数
                h = advanceProbe(h);
            }
            //cells为空,创建cells
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                boolean init = false;
                try {                           // Initialize table
                    if (cells == as) {
                        Cell[] rs = new Cell[2];
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            }
            else if (casBase(v = base, ((fn == null) ? v + x :
                                        fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }

同一个类所以简单画一个流程图

   

总结:

1.采用cells数组代替一个原子变量,使得多线程操作一个变量变化为多线程操作操作多个原子变量,提高了性能,

2.数组的连续存储,更容易导致伪共享,需要字节填充——@sun.misc.Contended注解,避免了伪共享

3.cells的创建、扩容及cell的创建利用了cellsBusy实现自旋锁保证了原子性

4.用当前线程的threadLocalRandomProbe作为hash值,以除余法为哈希函数求得当前线程访问Cell数组里的哪一个元素,需要注意的是扩容时并没有改变cells中cell的位置,即扩容后线程和cell的映射关系可能会改变

5.当多个线程访问了数组cells中同一个cell元素时,cells数组小于CPU个数时,并且发生扩容;cells数组达到最大值(CPU个数)时,只能rehash重新循环。

6.保险策略:当cells数组初始化失败时,用基值变量base作为单一原子变量执行,即回到原来的ActomicLong的单一原子变量value的逻辑。

另外:

    final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {
            ...
            else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
                break;
            ...
            else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
                break;
    }    

LongAdder中调用父类Striped64的longAccumulate(long x,LongBinaryOperator fn,boolean wasUncontended)方法时第二个参数fn传入null

LongAccumulator与LongAdder结构相同但可以传入自定义的fn,功能更加强大。

三、LongAccumulator类

 LongAccumulator与LongAdder的结构相同。

1.初始化与变量

    private final LongBinaryOperator function;//双目运算器接口,输入两个参数返回一个计算值
    private final long identity;//

    /**
     * Creates a new instance using the given accumulator function
     * and identity element.
     * @param accumulatorFunction a side-effect-free function of two arguments
     * @param identity identity (initial value) for the accumulator function
     */
    public LongAccumulator(LongBinaryOperator accumulatorFunction,
                           long identity) {
        this.function = accumulatorFunction;        base = this.identity = identity;    }

2.方法

    public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))
                longAccumulate(x, null, uncontended);
        }
    }

    public void accumulate(long x) {
        Cell[] as; long b, v, r; int m; Cell a;
        if ((as = cells) != null ||
            (r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) {
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended =
                  (r = function.applyAsLong(v = a.value, x)) == v ||
                  a.cas(v, r)))
                longAccumulate(x, function, uncontended);
        }
    }

1.LongAdder.add()与LongAccumulator.accumulate()逻辑基本一模一样,仅仅运算器不一样

2.LongAccumulator的双目运算器function不能为空,可动态指定运算器规则,所以LongAdder其实是LongAccumulator的一个特例,LongAccumulator功能更加强大

LongAdder adder = new LongAdder();
//accumulator相当于上面adder
LongAccmulator accumulator = new Long Accumulator(new LongBinaryOperator(){
    @Override
    public long applyAsLong(long left, long right){
        return left + right;//LongAccumulator功能更加强大表现在这里,还可以是left * right等自定义逻辑
    }
});

参考自《Java并发编程之美》

猜你喜欢

转载自www.cnblogs.com/wqff-biubiu/p/12159334.html