• 线程基础知识11原子类


    1 简介

      java中提供了一些原子类,原子类包装了一个变量,并且提供了一系列对变量进行原子性操作的方法。我们在多线程环境下对这些原子类进行操作时,不需要加锁,大大简化了并发编程的开发。
      目前Java中提供的原子类大部分底层使用了CAS锁(CompareAndSet自旋锁),如AtomicInteger、AtomicLong等;也有使用了分段锁+CAS锁的原子类,如LongAdder等。

    2 基本类型原子类

    2.1 简介

      包含以下三类,比较简单。

        AtomicInteger

        AtomicBoolean

        AtomicLong

     

    2.2 常用api

    1)public final int get()

      获取当前的值

     

    2)public final int getAndSet(int newValue)

      获取当前的值,并设置新的值

     

    3)public final int getAndIncrement()

      获取当前的值,并自增

     

    4)public final int getAndDecrement()

      获取当前的值,并自减

     

    5)public final int getAndAdd(int delta)

      获取当前的值,并加上预期的值

     

    6)boolean compareAndSet(int expect, int update)

      如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)

     

    2.3 示例

    public class AtomicTest1 {
    
       static class MyNumber {
            @Getter
            private AtomicInteger atomicInteger = new AtomicInteger();
            public void addPlusPlus()
            {
                atomicInteger.incrementAndGet();
            }
        }
        
           public static void main(String[] args) throws InterruptedException
        {
            MyNumber myNumber = new MyNumber();
            CountDownLatch countDownLatch = new CountDownLatch(100);
    
            for (int i = 1; i <=100; i++) {
                new Thread(() -> {
                    try
                    {
                        for (int j = 1; j <=5000; j++)
                        {
                            myNumber.addPlusPlus();
                        }
                    }finally {
                        countDownLatch.countDown();
                    }
                },String.valueOf(i)).start();
            }
    
            countDownLatch.await();
    
            System.out.println(myNumber.getAtomicInteger().get());
        }
    }

    3 数组类型原子类

    3.1 简介

       分为以下三类,api和基本类型原子类差不多

        AtomicIntegerArray

        AtomicLongArray

        AtomicReferenceArray

    3.2 示例

    public class AtomicTest2 {
        
         public static void main(String[] args)
        {
            //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
            //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
            AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});
    
            for (int i = 0; i <atomicIntegerArray.length(); i++) {
                System.out.println(atomicIntegerArray.get(i));
            }
    
            System.out.println(atomicIntegerArray.getAndSet(0,1122));
            System.out.println(atomicIntegerArray.get(0));
            System.out.println(atomicIntegerArray.getAndIncrement(1));
            System.out.println(atomicIntegerArray.get(1));
        }
    }

    4 引用类型原子类

    4.1 简介

      包含以下三类

        AtomicReference

        AtomicStampedReference

        AtomicMarkableReference

    4.2 AtomicReference 编写自旋锁示例

    public class AtomicTest3 {
    
        AtomicReference<Thread> atomicReference = new AtomicReference<>();
    
        public void myLock()
        {
            Thread thread = Thread.currentThread();
            System.out.println(Thread.currentThread().getName()+"\t come in");
            while(!atomicReference.compareAndSet(null,thread))
            {
    
            }
        }
    
        public void myUnLock()
        {
            Thread thread = Thread.currentThread();
            atomicReference.compareAndSet(thread,null);
            System.out.println(Thread.currentThread().getName()+"\t myUnLock over");
        }
    
        public static void main(String[] args)
        {
            AtomicTest3 spinLockDemo = new AtomicTest3();
    
            new Thread(() -> {
                spinLockDemo.myLock();
                //暂停一会儿线程
                try { TimeUnit.SECONDS.sleep( 5 ); } catch (InterruptedException e) { e.printStackTrace(); }
                spinLockDemo.myUnLock();
            },"A").start();
            //暂停一会儿线程,保证A线程先于B线程启动并完成
            try { TimeUnit.SECONDS.sleep( 1 ); } catch (InterruptedException e) { e.printStackTrace(); }
    
            new Thread(() -> {
                spinLockDemo.myLock();
                spinLockDemo.myUnLock();
            },"B").start();
        }
    }

    4.3 AtomicStampedReference 示例

      相较于AtomicReference,它携带版本号,可解决ABA问题

    public class AtomicTest4 {
    
        static AtomicInteger atomicInteger = new AtomicInteger(100);
        static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100,1);
    
        public static void main(String[] args)
        {
            abaProblem();
            abaResolve();
        }
    
        public static void abaResolve()
        {
            new Thread(() -> {
                int stamp = atomicStampedReference.getStamp();
                System.out.println("t3 ----第1次stamp  "+stamp);
                try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                atomicStampedReference.compareAndSet(100,101,stamp,stamp+1);
                System.out.println("t3 ----第2次stamp  "+atomicStampedReference.getStamp());
                atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
                System.out.println("t3 ----第3次stamp  "+atomicStampedReference.getStamp());
            },"t3").start();
    
            new Thread(() -> {
                int stamp = atomicStampedReference.getStamp();
                System.out.println("t4 ----第1次stamp  "+stamp);
                //暂停几秒钟线程
                try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
                boolean result = atomicStampedReference.compareAndSet(100, 20210308, stamp, stamp + 1);
                System.out.println(Thread.currentThread().getName()+"\t"+result+"\t"+atomicStampedReference.getReference());
            },"t4").start();
        }
    
        public static void abaProblem()
        {
            new Thread(() -> {
                atomicInteger.compareAndSet(100,101);
                atomicInteger.compareAndSet(101,100);
            },"t1").start();
    
            try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
    
            new Thread(() -> {
                atomicInteger.compareAndSet(100,20210308);
                System.out.println(atomicInteger.get());
            },"t2").start();
        }
    }

    4.4 AtomicMarkableReference

      和AtomicStampedReference相比,它不记录版本号,只做一个标记,标记是否被修改过

    public class AtomicTest5 {
    
    
        static AtomicMarkableReference<Integer> markableReference = new AtomicMarkableReference<>(100,false);
    
        public static void main(String[] args)
        {
            System.out.println("============AtomicMarkableReference不关心引用变量更改过几次,只关心是否更改过======================");
    
            new Thread(() -> {
                boolean marked = markableReference.isMarked();
                System.out.println(Thread.currentThread().getName()+"\t 1次版本号"+marked);
                
                try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
                
                markableReference.compareAndSet(100,101,marked,!marked);
                System.out.println(Thread.currentThread().getName()+"\t 2次版本号"+markableReference.isMarked());
                
                markableReference.compareAndSet(101,100,markableReference.isMarked(),!markableReference.isMarked());
                System.out.println(Thread.currentThread().getName()+"\t 3次版本号"+markableReference.isMarked());
            },"t5").start();
    
            new Thread(() -> {
                boolean marked = markableReference.isMarked();
                System.out.println(Thread.currentThread().getName()+"\t 1次版本号"+marked);
               
                //暂停几秒钟线程
                try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
                
                markableReference.compareAndSet(100,2020,marked,!marked);
                System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference()+"\t"+markableReference.isMarked());
            },"t6").start();
        }
    }

    5 对象的属性修改原子类

    5.1 简介

      以一种线程安全的方式操作非线程安全对象内的某些字段,但我们操作某些对象,只需要保证这个对象的某个或几个属性的操作的原子性,就可以使用它来,而不用去锁定整个对象

      包含以下几类

        AtomicIntegerFieldUpdater:原子更新对象中int类型字段的值

        AtomicLongFieldUpdater:原子更新对象中Long类型字段的值

        AtomicReferenceFieldUpdater:原子更新引用类型字段的值

    5.2 使用要求

      1)更新的对象属性必须使用 public volatile 修饰符。

      2)因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

    5.3 AtomicIntegerFieldUpdater示例

    public class AtomicTest6 {
    
        static class BankAccount {
    
            private String bankName = "CCB";//银行
    
            public volatile int money = 0;//钱数  我们要操作的属性 volatile修饰
    
            //两个参数:操作的对象的class对象和操作的属性名
            AtomicIntegerFieldUpdater<BankAccount> accountAtomicIntegerFieldUpdater =
                    AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");
            //不加锁 + 性能高
            public void transferMoney(BankAccount bankAccount) {
                accountAtomicIntegerFieldUpdater.incrementAndGet(bankAccount);
            }
        }
    
        public static void main(String[] args) {
            BankAccount bankAccount = new BankAccount();
            for (int i = 1; i <= 1000; i++) {
                int finalI = i;
                new Thread(() -> {
                    bankAccount.transferMoney(bankAccount);
                }, String.valueOf(i)).start();
            }
    
            //暂停毫秒,等待执行完成,也可以使用countdowmlatch
            try {
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(bankAccount.money);
        }
    }

    执行结果,结果是1000,正确

    1000

    5.4 AtomicIntegerFieldUpdater示例

    public class AtomicTest7 {
    
        static class MyVar{
            public volatile Boolean isInit = Boolean.FALSE;
            AtomicReferenceFieldUpdater<MyVar,Boolean> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");
    
    
            public void init(MyVar myVar)
            {
                if(atomicReferenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE))
                {
                    System.out.println(Thread.currentThread().getName()+"\t"+"---init.....");
                    //暂停几秒钟线程
                    try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println(Thread.currentThread().getName()+"\t"+"---init.....over");
                }else{
                    System.out.println(Thread.currentThread().getName()+"\t"+"------其它线程正在初始化");
                }
            }
        }
        public static void main(String[] args) throws InterruptedException {
            MyVar myVar = new MyVar();
            for (int i = 1; i <=5; i++) {
                new Thread(() -> {
                    myVar.init(myVar);
                },String.valueOf(i)).start();
            }
        }
    }

    执行结果

    1    ---init.....
    3    ------其它线程正在初始化
    2    ------其它线程正在初始化
    4    ------其它线程正在初始化
    5    ------其它线程正在初始化
    1    ---init.....over
    
    Process finished with exit code 0

    6 原子操作增强类

    6.1 简介

      包含以下几类

        DoubleAccumulator:DoubleAccumulator提供了自定义的函数操作

        DoubleAdder:DoubleAdder只能用来计算加法,且从零开始计算

        LongAccumulator:LongAccumulator提供了自定义的函数操作

        LongAdder:LongAdder只能用来计算加法,且从零开始计算

    6.2 示例

    public class AtomicTest8 {
    
         public static void main(String[] args)
        {
            LongAdder longAdder = new LongAdder(); //默认是0开始
    
            longAdder.increment(); // +1
            longAdder.increment(); // +1
            longAdder.increment(); // +1
            System.out.println(longAdder.longValue());
    
            //-------------------------------------------------------------------
    
            //两个参数,一个是自定义函数,一个是初始值,这里传入的函数式简单的乘法
            LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x * y,2);
            System.out.println(longAccumulator.longValue());
    
            longAccumulator.accumulate(1);
            System.out.println(longAccumulator.longValue());
    
            longAccumulator.accumulate(2);
            System.out.println(longAccumulator.longValue());
    
            longAccumulator.accumulate(3);
            System.out.println(longAccumulator.longValue());
        }
    }

    执行结果

    3
    2
    2
    4
    12

    6.3 效率对比示例

      50个线程,每个线程做100W次+1操作,比较synchronized、AtomicLong、LongAdder、LongAccumulator的执行效率

    public class AtomicTest9 {
    
        //synchronized  =============================
        int number = 0;
        public synchronized void clickBySync()
        {
            number++;
        }
    
        //AtomicLong  ================================
        AtomicLong atomicLong = new AtomicLong(0);
    
        public void clickByAtomicLong()
        {
            atomicLong.incrementAndGet();
        }
    
        //LongAdder  =================================
        LongAdder longAdder = new LongAdder();
        public void clickByLongAdder()
        {
            longAdder.increment();
        }
    
        //LongAccumulator  ===================================
        LongAccumulator longAccumulator = new LongAccumulator((x,y) -> x + y,0);
        public void clickByLongAccumulator()
        {
            longAccumulator.accumulate(1);
        }
    
        public static void main(String[] args) throws InterruptedException
        {
            AtomicTest9 clickNumberNet = new AtomicTest9();
    
            long startTime;
            long endTime;
            CountDownLatch countDownLatch = new CountDownLatch(50);
            CountDownLatch countDownLatch2 = new CountDownLatch(50);
            CountDownLatch countDownLatch3 = new CountDownLatch(50);
            CountDownLatch countDownLatch4 = new CountDownLatch(50);
    
            startTime = System.currentTimeMillis();
            for (int i = 1; i <=50; i++) {
                new Thread(() -> {
                    try
                    {
                        for (int j = 1; j <=100 * 10000; j++) {
                            clickNumberNet.clickBySync();
                        }
                    }finally {
                        countDownLatch.countDown();
                    }
                },String.valueOf(i)).start();
            }
            countDownLatch.await();
            endTime = System.currentTimeMillis();
            System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickBySync result: "+clickNumberNet.number);
    
            startTime = System.currentTimeMillis();
            for (int i = 1; i <=50; i++) {
                new Thread(() -> {
                    try
                    {
                        for (int j = 1; j <=100 * 10000; j++) {
                            clickNumberNet.clickByAtomicLong();
                        }
                    }finally {
                        countDownLatch2.countDown();
                    }
                },String.valueOf(i)).start();
            }
            countDownLatch2.await();
            endTime = System.currentTimeMillis();
            System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByAtomicLong result: "+clickNumberNet.atomicLong);
    
            startTime = System.currentTimeMillis();
            for (int i = 1; i <=50; i++) {
                new Thread(() -> {
                    try
                    {
                        for (int j = 1; j <=100 * 10000; j++) {
                            clickNumberNet.clickByLongAdder();
                        }
                    }finally {
                        countDownLatch3.countDown();
                    }
                },String.valueOf(i)).start();
            }
            countDownLatch3.await();
            endTime = System.currentTimeMillis();
            System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByLongAdder result: "+clickNumberNet.longAdder.sum());
    
            startTime = System.currentTimeMillis();
            for (int i = 1; i <=50; i++) {
                new Thread(() -> {
                    try
                    {
                        for (int j = 1; j <=100 * 10000; j++) {
                            clickNumberNet.clickByLongAccumulator();
                        }
                    }finally {
                        countDownLatch4.countDown();
                    }
                },String.valueOf(i)).start();
            }
            countDownLatch4.await();
            endTime = System.currentTimeMillis();
            System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t clickByLongAccumulator result: "+clickNumberNet.longAccumulator.longValue());
        }
    }

    执行结果

    ----costTime: 1774 毫秒     clickBySync result: 50000000
    ----costTime: 1053 毫秒     clickByAtomicLong result: 50000000
    ----costTime: 131 毫秒     clickByLongAdder result: 50000000
    ----costTime: 135 毫秒     clickByLongAccumulator result: 50000000
    
    Process finished with exit code 0

    发现LongAdder和LongAccumulator效率远高于AtomicLong,AtomicLong高于synchronized

    6.4 小结

    6.4.1 AtomicLong

      原理:CAS+自旋

      场景:低并发下的全局计算。AtomicLong能保证并发情况下计数的准确性,其内部通过CAS来解决并发安全性的问题。

      缺点:高并发后性能急剧下降。AtomicLong的自旋会成为瓶颈。

         N个线程CAS操作修改线程的值,每次只有一个成功过,其它N - 1失败,失败的不停的自旋直到成功,这样大量失败自旋的情况,一下子cpu就打高了。

    6.4.2 LongAdder

      原理:CAS+Base+Cell数组分散。空间换时间并分散了热点数据

      场景:高并发下的全局计算

    7 LongAdder解析

      通过对add方法的解析,知道为什么在并发较高,计算量大的情况下,LongAdder执行效率高

    7.1 原理简介

      像AtomicLong,高并发下很多线程都在进行cas操作,必然会有大量的cas失败操作,一致处于自旋状态,导致严重浪费CPU资源,降低了并发性。

      既然AtomicLong性能问题是由于过多线程同时去竞争同一个变量的更新而降低的,那么如果把一个变量分解为多个变量,让同样多的线程去竞争多个资源,就可以减少竞争,增加cas的成功率。

      简单来说,100个线程,从0开始,使用AtomicLong来做一个加1操作,每个线程操作10W次。那么就是100个线程去竞争操作AtomicLong的value值(private volatile long value;)。

      而LongAdder,它除了一个base值,还有一个Cell数组(transient volatile long base;transient volatile Cell[] cells;) Cell也可以理解为存储值的一个对象。

      100个线程来进行累加操作时,会先操作base,如果发生竞争失败,就会初始化cells数组,对cells数组里面的Cell的值进行累加操作,最后把base的值和cells数组里面的Cell的值合起来,就是最终的值。

      相比于AtomicLong操作一个value值,到了LongAdder这里操作base和cells数组里面的多个Cell,竞争就会小一些了,并发也就提升了

    LongAdder实现原理图

      LongAdder则是内部维护一个long类型的变量base和一个Cells数组类型的变量cells

      最开始cells为null,所有线程对base进行cas操作,当操作失败时,开始初始化cells,最开始是创建一个长度为2的cells数组,然后多个线程去争夺base和cells数组里面的cells,当还是不够时,对cells数组进行2倍扩容,最大扩容到计算机的CPU数。

      每个Cell里面有一个初始值为0的long型变量,在同等并发量的情况下,由于可以争夺base和cells数组里面的cell,线程被分散了,争夺单个变量的线程会减少,这是变相的减少了争夺共享资源的并发量,另外多个线程在争夺同一个原子变量时候,如果失败并不是自旋CAS重试,而是尝试获取其他原子变量的锁,最后当获取当前值时候是把所有变量的值累加后再加上base的值返回的。

      Cell 类型是Atomic的一个改进,用来减少缓存的争用,对于大多数原子操作字节填充是浪费的,因为原子操作都是无规律的分散在内存中进行的,多个原子性操作彼此之间是没有接触的,但是原子性数组元素彼此相邻存放将能经常共享缓存行,也就是伪共享。所以这在性能上是一个提升。

      

    7.2 继承关系

    继承了Striped64

    public class LongAdder extends Striped64 implements Serializable 

    Striped64又继承了Number

    abstract class Striped64 extends Number

    7.3 Striped64类

    7.3.1 简介
      Striped64是一个高并发累加的工具类。
      Striped64的设计核心思路就是通过内部的分散计算来避免竞争。 
      Striped64内部包含一个base和一个Cell[] cells数组,又叫hash表。 
      没有竞争的情况下,要累加的数通过cas累加到base上;如果有竞争的话,会将要累加的数累加到Cells数组中的某个cell元素里面和base上。所以整个Striped64的值为sum=base+∑[0~n]cells。

    7.3.2 核心属性说明


    1)transient volatile Cell[] cells;  // 存放cell的hash表,大小为2乘幂,最大为cpu数


    2)transient volatile long base;  // 基础值,在没有竞争的情况下,直接累加到base,或者cells初始化和扩容时,也要将数据累加到base上


    3)transient volatile int cellsBusy;  // 初始化cells和cells扩容时需要加锁,通过CAS操作加锁,0表示无锁状态,1表示其它线程已经持有锁

    4)static final int NCPU = Runtime.getRuntime().availableProcessors();  当前计算机cpu数量,cells扩容不会超过它

    7.3.3 内部类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);
                }
            }
        }

    7.4 add方法

    // 累加方法,参数x为累加的值    
    public void add(long x) {
            Cell[] as; long b, v; int m; Cell a;
            /**
             * 如果以下两种条件则继续执行if内的语句
             * 1. cells数组不为null,也就是cells已经完成初始化
             * 2. 如果cells数组为null(还没有完成初始化),先尝试操作base累加,如果casBase执行成功,则直接返回,如果casBase方法执行失败(说明争用冲突产生,需要对cells数组初始化)
             * casBase方法很简单,就是通过UNSAFE类的cas设置成员变量base的值为base+要累加的值
             */
            if ((as = cells) != null || !casBase(b = base, b + x)) {
                //uncontended是一个标识,判断cells数组中,当前线程要做cas累加操作的某个元素是否不存在争用;
           //uncontended=false代表存在争用,uncontended=true代表不存在争用。
    boolean uncontended = true; /** *1. as == null : cells数组未被初始化,成立则直接进入if执行cell初始化 *2. (m = as.length - 1) < 0: cells数组的长度为0,cells还没有完成初始化,完成初始化了长度应该是2 *3. (a = as[getProbe() & m]) == null :判断要操作的cells数组的那个元素是否已创建
              
    getProbe是去获取当前线程的hash值,再用它对m(cells的长度)取余,获取到要操作的cells的元素的下标,下标所在的这个元素是否为空,如果为空,说明这个cell还没有创建 *4. !(uncontended = a.cas(v = a.value, v + x)):尝试对cells数组下标位置的Cell对象中的value值做累加操作,如果失败了则进入if 如果进入if语句执行longAccumulate方法,有三种情况 1. 前两个条件代表cells没有初始化完成, 2. 第三个条件指当前线程hash到的cells数组中的位置还没有其它线程做过累加操作, 3. 第四个条件代表产生了冲突,uncontended=false **/ 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); } }

    7.5 longAccumulate方法

    7.5.1 longAccumulate的粗流程

      它是一个循环操作,里面有三条线

      第一个if判断,是cells已经完成初始化了可以进入,对cells某个元素进行累加操作或者进行cells扩容操作

      第二个判断,cells没有加锁,也没有进行初始化,可以进入,进去后,加锁,并初始化cells

      第三个判断,cells正在进行初始化,尝试对base进行累加操作

    7.5.2 longAccumulate源码

    final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {
            //获取当前线程的threadLocalRandomProbe值作为hash值,
         //如果当前线程的threadLocalRandomProbe为0,生成hash值
      //1.当前线程之前还没有参与过cells争用(也许cells数组还没初始化,进到当前方法来就是为了初始化cells数组后争用的),是第一次执行base的cas累加操作失败;   //2.或者是在执行add方法时,对cells某个位置的Cell的cas操作第一次失败,那里将wasUncontended设置为false,那么这里会将其重新置为true;再次开始尝试操作 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; //这个主干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) { /** *内部小分支一:这个是处理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 ,如果成功直接退出 */ else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) break; /** *内部小分支四:分支3处理新的线程争用执行失败了,这时如果cells数组的长度已经到了最大值(大于等于cup数量),或者是当前cells已经做了扩容,则将collide设置为false,后面重新计算prob的值*/ else if (n >= NCPU || cells != as) collide = false; /** *内部小分支五:如果发生了冲突collide=false,则设置其为true;会在最后重新计算hash值后,进入下一次for循环 */ else if (!collide) //设置冲突标志,表示发生了冲突,需要再次生成hash,重试。 如果下次重试任然走到了改分支此时collide=true,!collide条件不成立,则走后一个分支 collide = true; /** *内部小分支六:扩容cells数组,新参与cell争用的线程两次均失败,且符合库容条件,会执行该分支 */ else if (cellsBusy == 0 && casCellsBusy()) { try { //检查cells是否已经被扩容 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 } //为当前线程重新计算hash值 h = advanceProbe(h); //这个大的分支处理add方法中的条件1与条件2成立的情况,如果cell表还未初始化或者长度为0,先尝试获取cellsBusy锁。 }else if (cellsBusy == 0 && cells == as && casCellsBusy()) { boolean init = false; try { // Initialize table //初始化cells数组,初始容量为2,并将x值通过hash&1,放到0个或第1个位置上 if (cells == as) { Cell[] rs = new Cell[2]; rs[h & 1] = new Cell(x); cells = rs; init = true; } } finally { //解锁 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; } }

    7.5.3 流程图

  • 相关阅读:
    Aptana 由于没有关闭编辑器而导致的启动不起来了。
    postgresql备份导入数据库小记
    [转] js 事件冒泡 阻止
    ruby 取得ip
    [ 转 ] 网页聊天室的原理
    ryby 数组笔记
    第一个rails应用
    vue-router-5-命名路由
    vue-router-4-编程式导航
    vue-router-3-嵌套路由
  • 原文地址:https://www.cnblogs.com/jthr/p/16130477.html
Copyright © 2020-2023  润新知