java并发编程之美-阅读记录4

不打扰是莪最后的温柔 提交于 2019-11-26 20:01:45

java并发包中的原子操作类,这些类都是基于非阻塞算法CAS实现的。

4.1原子变量操作类

  AtomicInteger/AtomicLong/AtomicBoolean等原子操作类

  AtomicLong类:

public class AtomicLong extends Number implements java.io.Serializable {
    // 基于硬件的原子操作类
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // 存放value的偏移地址
    private static final long valueOffset;
    //判断jvm是否支持Long类型无锁CAS
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
    private static native boolean VMSupportsCS8();
    // 初始化value字段的偏移量
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }   // 存放具体的值
    private volatile long value;
.........    
}

虽然该类提供了原子操作(虽然是无阻塞的CAS操作,相对于阻塞算法提升了很火),但是在高并发情况下,会竞争更新同一个原子变量,仍然会有效率问题。

导致问题的原因也就是CAS操作:

public  final  long  getAndAddLong(Object  paramObject ,  long  paramLongl ,  long paramLong2)
        long  l ;
        do {
            1  =  getLongvolatile(paramObject ,  paramLongl) ;
                // .compareAndSwapLong比较交换操作,该方法就是CAS的核心方法,但是该方法在高并发情况下,会竞争更新原子变量,最终只有一个线程更新成功,其他线程会循环多次CAS操作,浪费了cpu资源,降低了效率
        )  while  (!compareAndSwapLong(param0bject ,  paramLongl ,  1,  1  +  paramLong2) );
        
        return  l ;
}compareAndSwapLong:paramObject操作对象,paramLong1 value的偏移量,第三个参数为except表示当前是否是该值,最后一个参数就是要修改成的值

 

jdk8新增的原子操作类LongAdder

   为了解决高并发情况下多线程对一个共享变量的CAS争夺失败后进行自旋而造成的降低并发性能的问题,LongAdder在内部维护了一个cell元素(一个动态的cell数组)来分担对单个变量进行争夺的开销,也就是将对一个变量的争夺分配到对多个变量的争夺上。

LongAdder结构:继承Striped64

Striped64类,Cell类上有一个Contented注解,作用是避免伪共享问题

  // 一个内部类,LongAdder就是通过Cell对象来提高性能,降低自选的CAS操作
 @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类,基于硬件的原子操作类
        private static final sun.misc.Unsafe UNSAFE;
        // value在Cell对象内存地址中的偏移量
        private static final long valueOffset;
        // 静态代码块 初始化Unsafe类,和偏移量valueOfset
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> ak = Cell.class;
                valueOffset = UNSAFE.objectFieldOffset
                    (ak.getDeclaredField("value"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

    /** Number of CPUS, to place bound on table size */
    static final int NCPU = Runtime.getRuntime().availableProcessors();

    // cell数组,每一个线程会争夺一个cell对象,cell数组变相的降低了争夺一个变量的性能问题
    transient volatile Cell[] cells;

    // LongAdder的真实值就是base+cell[0]...+cell[n]
    transient volatile long base;

    // 为了实现自旋锁,状态值只有0和1
    transient volatile int cellsBusy;

LongAdder重点方法add():

  当内部increment或decrement自增或自减操作是,内部调用的就是add方法,当cell数组为空时,默认走的就是casBase方法(此时和AtomicLong操作相同,即都是在base的基础上进行累加或累减操作的)。

  进入第3行if判断的情况:

    1:cells不为空,会进入if

      假如cells不为空进入的if判断,则第5行结果为false,第6行会判断Thread对象中的threadLocalRandomProbe的值是否存在,不存在,则直接调用longAccumulate方法,存在的话,会进一步判断a.cas操作是否成功,不成功则调用longAccumulate方法,成功则代表add方法累加成功

    2:casBase操作失败,会进入if

      如果cells为空,casBase方法操作失败,则进入if判断中,且第5行为true,则直接调用longAccumulate方法

 1     public void add(long x) {
 2         Cell[] as; long b, v; int m; Cell a;
 3         if ((as = cells) != null || !casBase(b = base, b + x)) {
 4             boolean uncontended = true;
 5             if (as == null || (m = as.length - 1) < 0 ||
 6                 (a = as[getProbe() & m]) == null ||
 7                 !(uncontended = a.cas(v = a.value, v + x)))
 8                 longAccumulate(x, null, uncontended);
 9         }
10     }
11 
12     /**
13      * Equivalent to {@code add(1)}.
14      */
15     public void increment() {
16         add(1L);
17     }
18 
19     /**
20      * Equivalent to {@code add(-1)}.
21      */
22     public void decrement() {
23         add(-1L);
24     }

 longAccumulate方法是进行cells数组初始化和扩容的地方

    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        int h;
        // 初始化probe,也就是Thread类中的threadlocalsRandomProbe变量
        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;
            // 这里和上边add方法的两种情况对应,一种是cells不为null,一种是未null,当前if为cells不为null的时候会进入
            if ((as = cells) != null && (n = as.length) > 0) {                // as[(n-1) & h] 里边的索引和上一步add方法中的相同,都是通过probe的值 并上 cells数组的长度减1,  这一块就是获取线程应该访问的Cell对象(相当于AtomicLong对象中的共享变量)
                if ((a = as[(n - 1) & h]) == null) { 
                    if (cellsBusy == 0) {       // Try to attach new Cell 当前索引处无Cell对象,并且没有线程在执行CAS操作,则会新建一个Cell对象
                        Cell r = new Cell(x);   // Optimistically create
                        if (cellsBusy == 0 && casCellsBusy()) {
                            boolean created = false;
                            try {               // Recheck under lock
                                Cell[] rs; int m, j;
                                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;
                }
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                             fn.applyAsLong(v, x))))
                    break;
                else if (n >= NCPU || cells != as)
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        if (cells == as) {      // Expand table unless stale
                            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
                }
                h = advanceProbe(h);
            }
            // 以下判断是当cells为null时,并且cellsBusy为0,并且能够将cellsBusy自增为1时,也就是能够加锁时,进入判断内
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                boolean init = false;
                try {                           // Initialize table
                    if (cells == as) { // 默认初始化Cell数组大小为2
                        Cell[] rs = new Cell[2];
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            }
            // 以上条件都不满足,如果指定了operation方法,则使用operation进行操作,没有的话,则进行累加操作(这块是针对自定义operation的)
            else if (casBase(v = base, ((fn == null) ? v + x :
                                        fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }    

 

 LongAccumulator类:LongAdder是LongAccumulator的一个特例,后者提供了更加强大的功能,可以让用户自定义累加规则

    public LongAccumulator(LongBinaryOperator accumulatorFunction,
                           long identity) {
        this.function = accumulatorFunction;
        base = this.identity = identity;
    }

// java8中的函数式接口可以自定义操作规则,累加、累乘等操作
@FunctionalInterface
public interface LongBinaryOperator {

    /**
     * Applies this operator to the given operands.
     *
     * @param left the first operand
     * @param right the second operand
     * @return the operator result
     */
    long applyAsLong(long left, long right);
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!