• 多线程-- 原子访问和atomic原子操作类实现原理


    (1)原子操作类的优点

    JUC包提供了一系列的原子性操作类,这些类都是使用非阻塞算法CAS实现的,相比使用锁实现原子性操作这在性能上有很大提高。包含有AtomicInteger、AtomicLong、AtomicBoolean等原子性操作类

    (2)学习AtomicLong

    AtomicLong是原子性递增或者递减类,其内部使用Unsafe来实现,源码如下:

    public class AtomicLong extends Number implements java.io.Serializable {
        private static final long serialVersionUID = 1927816293512124184L;
    
        // setup to use Unsafe.compareAndSwapLong for updates
        // (1)获取Unsafe实例
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        // (2)存放变量value的偏移量
        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.
         */
         // (3)判断JVM是否支持Long类型无锁CAS
        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 {
            	// (4)获取value在AtomicLong中的偏移量
                valueOffset = unsafe.objectFieldOffset
                    (AtomicLong.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
    
        // (5)实际变量值
        // 为了在多线程下保证内存可见性
        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() {
        }
    
        /**
         * Gets the current value.
         *
         * @return the current value
         */
        public final long get() {
            return value;
        }
    
        /**
         * Sets to the given value.
         *
         * @param newValue the new value
         */
        public final void set(long newValue) {
            value = newValue;
        }
    
        /**
         * Eventually sets to the given value.
         *
         * @param newValue the new value
         * @since 1.6
         */
        public final void lazySet(long newValue) {
            unsafe.putOrderedLong(this, valueOffset, newValue);
        }
    
        /**
         * Atomically sets to the given value and returns the old value.
         *
         * @param newValue the new value
         * @return the previous value
         */
        public final long getAndSet(long newValue) {
            return unsafe.getAndSetLong(this, valueOffset, newValue);
        }
    
        /**
         * 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);
        }
    
        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * 

    May fail * spuriously and does not provide ordering guarantees, so is * only rarely an appropriate alternative to {@code compareAndSet}. * * @param expect the expected value * @param update the new value * @return {@code true} if successful */ public final boolean weakCompareAndSet(long expect, long update) { return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } /** * Atomically increments by one the current value. * * @return the previous value */ 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); } /** * Atomically increments by one the current value. * * @return the updated value */ public final long incrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L; } /** * Atomically decrements by one the current value. * * @return the updated value */ public final long decrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L; } /** * Atomically adds the given value to the current value. * * @param delta the value to add * @return the updated value */ public final long addAndGet(long delta) { return unsafe.getAndAddLong(this, valueOffset, delta) + delta; } /** * Atomically updates the current value with the results of * applying the given function, returning the previous value. The * function should be side-effect-free, since it may be re-applied * when attempted updates fail due to contention among threads. * * @param updateFunction a side-effect-free function * @return the previous value * @since 1.8 */ public final long getAndUpdate(LongUnaryOperator updateFunction) { long prev, next; do { prev = get(); next = updateFunction.applyAsLong(prev); } while (!compareAndSet(prev, next)); return prev; } /** * Atomically updates the current value with the results of * applying the given function, returning the updated value. The * function should be side-effect-free, since it may be re-applied * when attempted updates fail due to contention among threads. * * @param updateFunction a side-effect-free function * @return the updated value * @since 1.8 */ public final long updateAndGet(LongUnaryOperator updateFunction) { long prev, next; do { prev = get(); next = updateFunction.applyAsLong(prev); } while (!compareAndSet(prev, next)); return next; } /** * Atomically updates the current value with the results of * applying the given function to the current and given values, * returning the previous value. The function should be * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function * is applied with the current value as its first argument, * and the given update as the second argument. * * @param x the update value * @param accumulatorFunction a side-effect-free function of two arguments * @return the previous value * @since 1.8 */ 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; } /** * Atomically updates the current value with the results of * applying the given function to the current and given values, * returning the updated value. The function should be * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function * is applied with the current value as its first argument, * and the given update as the second argument. * * @param x the update value * @param accumulatorFunction a side-effect-free function of two arguments * @return the updated value * @since 1.8 */ 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; } /** * Returns the String representation of the current value. * @return the String representation of the current value */ public String toString() { return Long.toString(get()); } /** * Returns the value of this {@code AtomicLong} as an {@code int} * after a narrowing primitive conversion. * @jls 5.1.3 Narrowing Primitive Conversions */ public int intValue() { return (int)get(); } /** * Returns the value of this {@code AtomicLong} as a {@code long}. */ public long longValue() { return get(); } /** * Returns the value of this {@code AtomicLong} as a {@code float} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public float floatValue() { return (float)get(); } /** * Returns the value of this {@code AtomicLong} as a {@code double} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public double doubleValue() { return (double)get(); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304

    (1)递增和递减操作代码

    // (6)调用unsafe方法,原子性设置value值为原始值+1,返回值为递增后的值
    public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
    }
    
    // (7)调用unsafe方法,原子性设置value值为原始值-1,返回值为递减后的值
    public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }
    
    // (8)调用unsafe方法,原子性设置value值为原始值+1,返回值为原始值
    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }
    
    // (9)调用unsafe方法,原子性设置value值为原始值-1,返回值为原始值
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在上面方法的内部都是调用Unsafe的getAndAddLong方法来实现操作的,这个函数是个原子性操作。

    (1)参数解析
    getAndAddLong(this, valueOffset, -1L);
    this:是AtomicLong实例的引用
    valueOffset:是value变量在AtomicLong中的偏移值
    -1L:是要设置的第二个变量的值

    getAndIncrement方法在JDK7中的实现逻辑

    public final long getAndInceament() {
    	while(true){
    		long current = get();
    		long next = current + 1;
    		if (comareAndSet(current,next))
    			return current;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (2)方法原理分析
    在上面的代码,每个线程是先拿到变量的当前值,因为value是volatile变量,所以拿到的值是从主内存中取出来的最新值,然后再工作内存中对其进行增1操作,而后使用CAS修改变量的值,如果设置失败,则循环继续尝试,直到设置成功。

    (2)public final boolean compareAndSet(long expect, long update) 方法

    public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }
    
    • 1
    • 2
    • 3

    在这个方法内部调用了Unsafe的compareAndSwapLong方法,如果原子变量中的value值等于expect,则使用update值更新该值并返回true,否则返回false

    多线程使用AtomicLong统计0额个数的例子

    public class TestThreadAtom03 {
    
        //创建long型原子计数器
        private static AtomicLong atomicLong = new AtomicLong();
        //创建数据源
        private static Integer[] arrayOne = new Integer[]{0,1,2,3,4,0,5,6,0,56};
        private static Integer[] arrayTwo = new Integer[]{10,1,2,3,4,0,5,6,0,56};
    
        public static void main(String[] args) throws InterruptedException {
            Thread threadOne = new Thread(new Runnable() {
                @Override
                public void run() {
                    int size = arrayOne.length;
                    for (int i = 0; i < size; ++i) {
                        if(arrayOne[i].intValue()==0){
                            atomicLong.incrementAndGet();
                        }
                    }
                }
            });
    
            Thread threadTwo = new Thread(new Runnable() {
                @Override
                public void run() {
                    int size = arrayTwo.length;
                    for (int i = 0; i < size; ++i) {
                        if(arrayTwo[i].intValue()==0){
                            atomicLong.incrementAndGet();
                        }
                    }
                }
            });
    
            threadOne.start();
            threadTwo.start();
    
            threadOne.join();
            threadTwo.join();
    
            System.out.println("所有数组中0的个数:"+atomicLong.get());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    执行结果为
    在这里插入图片描述

    (3)原子类的一些操作案例

    (4)JDK8中新增的原子操作类LongAdder

    (1)LongAdder和AtomicLong的区别

    AtomicLong通过CAS提供了非阻塞的原子性操作,相比使用阻塞算法的同步器来说它的性能已经很好了,但是AtomicLong在高并发下大量线程会同时去竞争更新同一个原子变量,但是由于同时只有一个线程的CAS操作会成功,这就造成了大量贤臣竞争失败后,会通过无限循环不断进行自旋尝试CAS操作,而这就会白白浪费CPU资源。

    JDK8中新增了一个原子性递增或者递减类LongAdder用来克服在高并发下使用AtomicLong的缺点。既然AtomicLong的性能瓶颈是由于过多线程同时去竞争一个变量的更新而产生的,那么如果把一个变量分解为多个变量,让同样多的线程去竞争多个资源
    在这里插入图片描述

    (2)LongAdder的实现原理

    在这里插入图片描述
    原来的AtomicLong,无论有多少线程访问,都是对一个原子变量进行竞争然后进行修改。现在LongAdder提供一个延迟初始化额原子性更新数组和衣蛾基值变量base,这个数组可以存放多个Cell,默认情况下这个Cell数组是null的,因为Cell占用的内存也挺大的,所以一开始不会创建它,而是在需要的时候再创建,也就是惰性加载,这样可以避免内存空间浪费。

    然后每个Cell里面有一个初始值为0的long型变量,也就相当于一个计数器,当一个线程成功竞争到一个Cell后,就会对Cell里面的计数器值进行修改,最后的最后把每个Cell里面的计数器value值累加再加上base返回,就是最终的结果值。

    这样设计的好处就是多个线程可以竞争的选择由以前的单个变成了多个,减少了争夺共享资源的并发量。并且如果一个线程竞争一个Cell原子变量时失败了,这个线程不会在当前这个Cell变量上一直自旋CAS尝试,而是转头去尝试在其他Cell的变量上进行CAS尝试,选择性增多了则线程重试CAS成功的可能性也提高了。

    (3)LongAdder的代码分析

    (1)Cell的构造源码

    // 为提高性能,使用注解@sun.misc.Contended,用来避免伪共享
     // 伪共享简单来说就是会破坏其它线程在缓存行中的值,导致重新从主内存读取,降低性能。
     @sun.misc.Contended static final class Cell {
            //用来保存要累加的值
            volatile long value;
            Cell(long x) { value = x; }
            //使用UNSAFE类的cas来更新value值
            final boolean cas(long cmp, long val) {
                return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
            }
            private static final sun.misc.Unsafe UNSAFE;
            //value在Cell类中存储位置的偏移量;
            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);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    (1)value用volatile修饰,保证变量的内存可见性
    (2)cas函数通过CAS操作,保证了当前线程更新时被分配的Cell元素汇总value值的原子性
    (3)Cell类用@sun.misc.Contended修饰是为了避免伪共享

    (2)当前线程应该访问Cell数组里面的哪一个Cell元素?

    (3)如何初始化Cell数组?

    final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {
            //获取当前线程的threadLocalRandomProbe值作为hash值,如果当前线程的threadLocalRandomProbe为0,说明当前线程是第一次进入该方法,则强制设置线程的threadLocalRandomProbe为ThreadLocalRandom类的成员静态私有变量probeGenerator的值,后面会详细将hash值的生成;
            //另外需要注意,如果threadLocalRandomProbe=0,代表新的线程开始参与cell争用的情况
            //1.当前线程之前还没有参与过cells争用(也许cells数组还没初始化,进到当前方法来就是为了初始化cells数组后争用的),是第一次执行base的cas累加操作失败;
            //2.或者是在执行add方法时,对cells某个位置的Cell的cas操作第一次失败,则将wasUncontended设置为false,那么这里会将其重新置为true;第一次执行操作失败;
            //凡是参与了cell争用操作的线程threadLocalRandomProbe都不为0;
            //(6)初始化当前线程的变量threadLocalRandomProbe的值
            int h;
            if ((h = getProbe()) == 0) {
                //初始化ThreadLocalRandom;
                ThreadLocalRandom.current(); // force initialization
                //将h设置为0x9e3779b9
                h = getProbe();
                //设置未竞争标记为true
                wasUncontended = true;
            }
            //cas冲突标志,表示当前线程hash到的Cells数组的位置,做cas累加操作时与其它线程发生了冲突,cas失败;collide=true代表有冲突,collide=false代表无冲突 
            boolean collide = false; 
            for (;;) {
                Cell[] as; Cell a; int n; long v;
                //(7)
                //这个主干if有三个分支
                //1.主分支一:处理cells数组已经正常初始化了的情况(这个if分支处理add方法的四个条件中的3和4)
                //2.主分支二:处理cells数组没有初始化或者长度为0的情况;(这个分支处理add方法的四个条件中的1和2)
                //3.主分支三:处理如果cell数组没有初始化,并且其它线程正在执行对cells数组初始化的操作,及cellbusy=1;则尝试将累加值通过cas累加到base上
                //先看主分支一
                if ((as = cells) != null && (n = as.length) > 0) {
                	//(8)
                    /**
                     *内部小分支一:这个是处理add方法内部if分支的条件3:如果被hash到的位置为null,说明没有线程在这个位置设置过值,没有竞争,可以直接使用,则用x值作为初始值创建一个新的Cell对象,对cells数组使用cellsBusy加锁,然后将这个Cell对象放到cells[m%cells.length]位置上 
                     */
                    if ((a = as[(n - 1) & h]) == null) {
                        //cellsBusy == 0 代表当前没有线程cells数组做修改
                        if (cellsBusy == 0) {
                            //将要累加的x值作为初始值创建一个新的Cell对象,
                            Cell r = new Cell(x); 
                            //如果cellsBusy=0无锁,则通过cas将cellsBusy设置为1加锁
                            if (cellsBusy == 0 && casCellsBusy()) {
                                //标记Cell是否创建成功并放入到cells数组被hash的位置上
                                boolean created = false;
                                try {
                                    Cell[] rs; int m, j;
                                    //再次检查cells数组不为null,且长度不为空,且hash到的位置的Cell为null
                                    if ((rs = cells) != null &&
                                        (m = rs.length) > 0 &&
                                        rs[j = (m - 1) & h] == null) {
                                        //将新的cell设置到该位置
                                        rs[j] = r;
                                        created = true;
                                    }
                                } finally {
                                    //去掉锁
                                    cellsBusy = 0;
                                }
                                //生成成功,跳出循环
                                if (created)
                                    break;
                                //如果created为false,说明上面指定的cells数组的位置cells[m%cells.length]已经有其它线程设置了cell了,继续执行循环。
                                continue;
                            }
                        }
                       //如果执行的当前行,代表cellsBusy=1,有线程正在更改cells数组,代表产生了冲突,将collide设置为false
                        collide = false;
     
                    /**
                     *内部小分支二:如果add方法中条件4的通过cas设置cells[m%cells.length]位置的Cell对象中的value值设置为v+x失败,说明已经发生竞争,将wasUncontended设置为true,跳出内部的if判断,最后重新计算一个新的probe,然后重新执行循环;
                     */
                    } else if (!wasUncontended)  
                        //设置未竞争标志位true,继续执行,后面会算一个新的probe值,然后重新执行循环。 
                        wasUncontended = true;
                    /**
                    *内部小分支三:新的争用线程参与争用的情况:处理刚进入当前方法时threadLocalRandomProbe=0的情况,也就是当前线程第一次参与cell争用的cas失败,这里会尝试将x值加到cells[m%cells.length]的value ,如果成功直接退出  
                    */
                    //(9)当前Cell存在,则执行CAS设置
                    else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                                 fn.applyAsLong(v, x))))
                        break;
                    /**
                     *内部小分支四:分支3处理新的线程争用执行失败了,这时如果cells数组的长度已经到了最大值(大于等于cup数量),或者是当前cells已经做了扩容,则将collide设置为false,后面重新计算prob的值*/
                    //(10)当前Cell数组元素个数大于CPU个数
                    else if (n >= NCPU || cells != as)
                        collide = false;
                    /**
                     *内部小分支五:如果发生了冲突collide=false,则设置其为true;会在最后重新计算hash值后,进入下一次for循环
                     */
                    //(11)是否有冲突
                    else if (!collide)
                        //设置冲突标志,表示发生了冲突,需要再次生成hash,重试。 如果下次重试任然走到了改分支此时collide=true,!collide条件不成立,则走后一个分支
                        collide = true;
                    /**
                     *内部小分支六:扩容cells数组,新参与cell争用的线程两次均失败,且符合库容条件,会执行该分支
                     */
                    //(12)如果当前元素个数没有达到CPU个数并且有冲突则扩容
                    else if (cellsBusy == 0 && casCellsBusy()) {
                        try {
                            //检查cells是否已经被扩容
                            if (cells == as) {      // Expand table unless stale
                                //(12.1)
                                Cell[] rs = new Cell[n << 1];
                                for (int i = 0; i < n; ++i)
                                    rs[i] = as[i];
                                cells = rs;
                            }
                        } finally {
                            //(12.2)
                            cellsBusy = 0;
                        }
                        //(12.3)
                        collide = false;
                        continue;                   // Retry with expanded table
                    }
                    //为当前线程重新计算hash值
                    //(13)为了能找到一个空闲Cell,重新计算hash值,xorshift算法生成随机数
                    h = advanceProbe(h);
     
                //这个大的分支处理add方法中的条件1与条件2成立的情况,如果cell表还未初始化或者长度为0,先尝试获取cellsBusy锁。
                //(14)初始化Cell数组
                }else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                    boolean init = false;
                    try {  // Initialize table
                        //初始化cells数组,初始容量为2,并将x值通过hash&1,放到0个或第1个位置上
                        if (cells == as) {
                        	//(14.1)初始化cells数组,初始容量为2
                            Cell[] rs = new Cell[2];
                            //(14.2)将x值通过hash&1,放到0个或第1个位置上
                            rs[h & 1] = new Cell(x);
                            cells = rs;
                            init = true;
                        }
                    } finally {
                        //(14.3)解锁
                        cellsBusy = 0;
                    }
                    //如果init为true说明初始化成功,跳出循环
                    if (init)
                        break;
                }
                /**
                 *如果以上操作都失败了,则尝试将值累加到base上;
                 */
                else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) // Fall back on using base
                    break;  
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144

    (6):会初始化当前线程变量threadLocalRandomProbe的值,这个变量在计算当前线程应该被分配到cells数组额哪一个Cell元素时会用到
    (14):cells数组的初始化,cellBusy是一个标示,为0说明当前cells数组没有在被初始化或者扩容,也没有在新建Cell元素,为1则说明cells数组在被初始化或者扩容,或者当前在创建新的Cell元素、通过CAS操作来进行0或1状态的切换
    (14.1)假设当前线程通过CAS设置cellsBusy为1,则当前线程开始初始化操作,那么这时候其他线程就不能进行扩容了。初始化cells数组元素个数为2,然后使用h&1计算当前线程应该访问cells数组的哪个位置,然后标示cells数组已经被初始化。
    (14.3)重置了cellsBusy标记

    (4)Cell数组如何扩容?

    (12)当代码(10)(11)的条件都不满足的时候,就是说当前cells的元素个数小于当前机器CPU个数并且当前多个线程访问了cells中同一个元素,从而导致冲突使其中一个线程CAS失败时才会进行扩容操作。
    先通过CAS设置cellsBusy为1,然后才能进行扩容
    (12.1)假设CAS成功则执行代码(12.1)把容量扩充为之前的2倍,并复制Cell元素到扩容后数组。另外,扩容后cells数组里面除了包含复制过来的元素外,还包含其他新元素,这些元素的值目前还是null

    (5)线程访问分配的Cell元素有冲突后如何处理?

    (13)对CAS失败的线程重新计算当前线程的随机值threadLocalRandomProbe,以减少下次访问cells元素时的冲突机会

    (6)如何保证线程操作被分配的Cell元素的原子性?

    (4)LongAdder的使用案例

    (5)LongAccumulator类原理和使用案例

    (6)原子类及其方法汇总

    在这里插入图片描述

    (1)原子基本类型

    AtomicBoolean
    AtomicLong
    AtomicInteger

    getAndIncrement 先使用再自增1,相当于 n++
    getAndDecrement 先使用,再自减1,相当于 n–
    decrementAndGet 先减1,再使用,相当于–n
    incrementAndGet 先加1,在使用。相当于 ++n
    get 获取当前的值

    package com.ljessie.sinopsis.atomic;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * 原子的Integer型
     * @author zbw
     *
     */
    public class AtomicIntegerDemo {
        
        private static AtomicInteger num = new AtomicInteger(0);
        
        public static void increate() {
            num.incrementAndGet();
        }
        
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread() {
                    public void run() {
                        for (int j = 0; j < 10; j++) {
                            try {
                                increate();
                                System.out.println(num);
                                Thread.sleep(200);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            
                        }
                    }
                }.start();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    (2)原子数组类型

    AtomicIntegerArray
    AtomicLongArray

    (3)原子引用类型

    AtomicReference
    AtomicStampedReference

    (4)原子属性类型

    AtomicLongFieldUpdater
    AtomicIntegerFieldUpdater

  • 相关阅读:
    拼多多助农 商业之外的底色
    Observability:使用 Elastic Observability 摄取和分析 Prometheus 指标
    基于Layui的提示框插件,layMin图片预览,加载中
    同样是IT行业,测试和开发薪资真有这么大差别?
    nodejs+vue游戏测评交流系统elementui
    git撤销操作(git后悔药)
    SQL Server底层架构技术对比
    少儿编程 电子学会图形化 scratch编程等级考试四级真题答案解析(判断题)2022年9月
    002 OpenCV dft 傅里叶变换
    FALSK+vue高校学生宿舍管理系统python-django
  • 原文地址:https://blog.csdn.net/weixin_44823875/article/details/127739425