JUC atomic原子操作类之18罗汉增强温习

JUC下的atomic打头的原子类相信大家并不陌生,温习下API,用代码证明阿里手册为啥建议使用LongAdder,它为什么就快!!!

  1. AtomicBoolean
  2. AtomicInteger
  3. AtomicIntegerArray
  4. AtomicIntegerFieldUpdater
  5. AtomicLong
  6. AtomicLongArray
  7. AtomicLongFieldUpdater
  8. AtomicMarkableReference
  9. AtomicReference
  10. AtomicReferenceArray
  11. AtomicReferenceFieldUpdater
  12. AtomicStampedReference
  13. DoubleAccumulator
  14. DoubleAdder
  15. LongAccumulator
  16. LongAdder
  17. Striped64
  18. Number

基本类型原子类

3个:AtomicInteger、AtomicBoolean、AtomicLong

常用API介绍:

public final int get(): 获取当前的值

public final int getAndSet(int newValue):获取当前的值,并设置新的值

public final int getAndIncrement():获取当前的值,并自增

public final int getAndDecrement():获取当前的值,并自减

public final int getAndAdd(int delta):获取当前的值,并加上预期的值

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

示例代码:

import lombok.Getter;
import lombok.SneakyThrows;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * AtomicIntegerDemo
 * 基本类型原子类case:AtomicInteger、AtomicBoolean、AtomicLong
 *
 * @author lcry
 * @date 2021/09/24 21:50
 */
public class AtomicIntegerDemo {

    //初始值1
    private static final AtomicInteger atomicInteger = new AtomicInteger(1);
    //默认值false
    private static final AtomicBoolean atomicBoolean = new AtomicBoolean(Boolean.FALSE);
    //默认值1
    private static final AtomicLong atomicLong = new AtomicLong(1L);

    public static void main(String[] args) {
        System.out.println("初始值:" + atomicInteger.get());
        System.out.println("获取当前值并设置新值:" + atomicInteger.getAndSet(2));
        System.out.println("获取当前值并自增:" + atomicInteger.getAndIncrement());
        System.out.println("获取当前值并自减:" + atomicInteger.getAndDecrement());
        System.out.println("获取当前值并加上期望值:" + atomicInteger.getAndAdd(10));
        System.out.println("如果输入的数值等于预期值,则以原子方式将该值设置为输入值:" + atomicInteger.compareAndSet(12, 2021));

        System.out.println("--------------");
        System.out.println("初始值:" + atomicBoolean.get());
        atomicBoolean.set(Boolean.TRUE);
        System.out.println(atomicBoolean.getAndSet(Boolean.FALSE));
        System.out.println(atomicBoolean.compareAndSet(Boolean.FALSE, Boolean.TRUE));

        System.out.println("--------------");
        System.out.println("初始值:" + atomicLong.get());
        System.out.println("获取当前值并设置新值:" + atomicLong.getAndSet(2L));
        System.out.println("获取当前值并自增:" + atomicLong.getAndIncrement());
        System.out.println("获取当前值并自减:" + atomicLong.getAndDecrement());
        System.out.println("获取当前值并加上期望值:" + atomicLong.getAndAdd(10L));
        System.out.println("如果输入的数值等于预期值,则以原子方式将该值设置为输入值:" + atomicLong.compareAndSet(12L, 2021L));

    }

    /**
     * 综合案例,多线程累加
     */
    @SneakyThrows
    private static void m1() {
        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());
    }
}

class MyNumber {
    @Getter
    private final AtomicInteger atomicInteger = new AtomicInteger();

    public void addPlusPlus() {
        atomicInteger.incrementAndGet();
    }
}

数组类型原子类

3个:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

参考基本类型原子类,示例代码如下:

/**
 * AtomicIntegerArrayDemo
 * 数组类型原子类case:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
 *
 * @author lcry
 * @date 2021/09/24 22:09
 */
public class AtomicIntegerArrayDemo {

    //初始化5个容量的数组,默认值都是0
    private static final AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
    //初始10个容量的数组
    private static final AtomicLongArray atomicLongArray = new AtomicLongArray(10);
    //初始3个person
    private static final AtomicReferenceArray<Person> atomicReferenceArray = new AtomicReferenceArray<>(3);

    public static void main(String[] args) {
        System.out.println("初始数据:" + atomicIntegerArray);
        atomicIntegerArray.set(0, 0);
        atomicIntegerArray.set(1, 1);
        System.out.println("数组长度:" + atomicIntegerArray.length());
        System.out.println("修改数组下标我为0个元素:" + atomicIntegerArray.compareAndSet(0, 0, 10));
        System.out.println("下标0元素值为:" + atomicIntegerArray.get(0));
        System.out.println("下标4元素值为:" + atomicIntegerArray.get(4));


        System.out.println("--------");
        System.out.println("初始数据:" + atomicLongArray);
        atomicIntegerArray.set(0, 0);
        atomicIntegerArray.set(1, 1);
        System.out.println("数组长度:" + atomicLongArray.length());
        System.out.println("修改数组下标我为0个元素:" + atomicLongArray.compareAndSet(0, 0, 10));
        System.out.println("下标0元素值为:" + atomicLongArray.get(0));
        System.out.println("下标4元素值为:" + atomicLongArray.get(4));

        System.out.println("--------");
        System.out.println("初始数据:" + atomicReferenceArray);
        Person p1 = new Person("张三", 10);
        atomicReferenceArray.set(0, p1);
        atomicReferenceArray.set(1, new Person("李四", 20));
        System.out.println("获取并设置: " + atomicReferenceArray.getAndSet(2, new Person("王五", 30)));
        System.out.println("对比并设置" + atomicReferenceArray.compareAndSet(0, p1, new Person("张三", 50)));
        System.out.println("最终数据:" + atomicReferenceArray);

    }
}

@Data
@AllArgsConstructor
class Person {
    private String name;
    private Integer age;
}

引用类型原子类

3个:AtomicReference、AtomicStampedReference、AtomicMarkableReference

还记得前面写的解决ABA问题吗?

/**
 * ABAProblemDemo
 * 演示CAS引发的ABA问题以及解决方案
 *
 * @author lcry
 * @date 2021/09/24 21:13
 */
public class ABAProblemDemo {
    static AtomicInteger atomicInteger = new AtomicInteger(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
    static AtomicMarkableReference<Integer> markableReference = new AtomicMarkableReference<>(100, false);
    static CountDownLatch countDownLatch = new CountDownLatch(2);

    @SneakyThrows
    public static void main(String[] args) {
        System.out.println("============以下是ABA问题的解决=============================");

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t 首次版本号:" + stamp);//1
            //暂停一会儿线程,
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t 2次版本号:" + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t 3次版本号:" + atomicStampedReference.getStamp());
            countDownLatch.countDown();
        }, "t3").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t 首次版本号:" + stamp);//1
            //暂停一会儿线程,获得初始值100和初始版本号1,故意暂停3秒钟让t3线程完成一次ABA操作产生问题
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100, 2021, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "\t" + result + "\t" + atomicStampedReference.getReference());
            countDownLatch.countDown();
        }, "t4").start();

        countDownLatch.await();

        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(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            markableReference.compareAndSet(100, 2021, marked, !marked);
            System.out.println(Thread.currentThread().getName() + "\t" + markableReference.getReference() + "\t" + markableReference.isMarked());
        }, "t6").start();

    }

    /**
     * aba问题浮现
     */
    private static void aba() {
        new Thread(() -> {
            atomicInteger.compareAndSet(100, 101);
            atomicInteger.compareAndSet(101, 100);
        }, "t1").start();

        new Thread(() -> {
            //暂停一会儿线程
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicInteger.compareAndSet(100, 2021) + "\t" + atomicInteger.get());
        }, "t2").start();

        //暂停一会儿线程,main彻底等待上面的ABA出现演示完成。
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

AtomicStampedReference:携带版本号的引用类型原子类,可以解决ABA问题,状态戳原子引用解决修改过几次

AtomicMarkableReference:原子更新带有标记位的引用类型对象,它的定义就是将状态戳简化为true|false,类似一次性筷子,不关心引用变量更改过几次,只关心是否更改过

对象的属性修改原子类

3个:AtomicIntegerFieldUpdater(原子更新对象中int类型字段的值)、AtomicLongFieldUpdater(原子更新对象中Long类型字段的值)、AtomicReferenceFieldUpdater(原子更新引用类型字段的值)

使用目的:以一种线程安全的方式操作非线程安全对象内的某些字段

使用要求:

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

示例代码:

/**
 * AtomicIntegerFieldUpdaterDemo
 * 以一种线程安全的方式操作非线程安全对象的某些字段。
 * 需求:
 * 1000个人同时向一个账号转账一元钱,那么累计应该增加1000元,
 * 除了synchronized和CAS,还可以使用AtomicIntegerFieldUpdater来实现。
 *
 * @author lcry
 * @date 2021/09/24 22:37
 */
public class AtomicIntegerFieldUpdaterDemo {
    public static void main(String[] args) {
        BankAccount bankAccount = new BankAccount();

        for (int i = 1; i <= 1000; i++) {
            new Thread(() -> {
                bankAccount.transferMoney(bankAccount);
            }, String.valueOf(i)).start();
        }

        //暂停毫秒
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(bankAccount.money);

    }
}

class BankAccount {
    //更新钱数
    static final AtomicIntegerFieldUpdater<BankAccount> accountAtomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");
    public volatile int money = 0;//钱数
    private String bankName = "CCB";//银行

    //不加锁+性能高,局部微创
    public void transferMoney(BankAccount bankAccount) {
        accountAtomicIntegerFieldUpdater.incrementAndGet(bankAccount);
    }
}
/**
 * AtomicReferenceFieldUpdaterDemo
 * 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作,要求只能初始化一次
 *
 * @author lcry
 * @date 2021/09/24 22:40
 */
public class AtomicReferenceFieldUpdaterDemo {
    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();
        }
    }

}

class MyVar {
    static final AtomicReferenceFieldUpdater<MyVar, Boolean> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, "isInit");
    //默认未初始化
    public volatile Boolean isInit = Boolean.FALSE;

    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" + "------其它线程正在初始化");
        }
    }

}

原子操作增强类原理深度解析

4个:DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder

常用API介绍
import java.math.BigDecimal;
import java.util.concurrent.atomic.DoubleAccumulator;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.DoubleBinaryOperator;
import java.util.function.LongBinaryOperator;

/**
 * LongAdderAPI
 * LongAdder常用API
 * LongAdder、LongAccumulator、DoubleAdder、DoubleAccumulator
 *
 * @author lcry
 * @date 2021/09/25 10:46
 */
public class LongAdderAPI {
    public static void main(String[] args) {

        //DoubleAdder只能用来计算加法,且从零开始计算
        DoubleAdder doubleAdder = new DoubleAdder();

        //加指定值
        doubleAdder.add(2d);
        doubleAdder.add(-2d);

        //返回当前值,特别注意:在没有并发的情况下返回的准确值,在高并发情况下返回的不能保证准确性(瞬间值)
        System.out.println(doubleAdder.sum());


        //重置doubleAdder的值,相当于重新new了一个DoubleAdder
        doubleAdder.reset();

        //获取当前value的值并重置
        System.out.println(doubleAdder.sumThenReset());

        //longValue就是调用的longAdder.sum()方法
        System.out.println(doubleAdder.doubleValue());

        //LongAccumulator提供了自定义的函数操作
        DoubleAccumulator doubleAccumulator = new DoubleAccumulator(new DoubleBinaryOperator() {

            //相加
            @Override
            public double applyAsDouble(double left, double right) {
                BigDecimal bd1 = new BigDecimal(Double.toString(left));
                BigDecimal bd2 = new BigDecimal(Double.toString(right));
                return bd1.add(bd2).doubleValue();
            }
        }, 100d);

        doubleAccumulator.accumulate(1.1d);
        doubleAccumulator.accumulate(2.2d);
        doubleAccumulator.accumulate(3.3d);

        System.out.println(doubleAccumulator.doubleValue());

    }

    private static void longAPI() {
        //LongAdder只能用来计算加法,且从零开始计算
        LongAdder longAdder = new LongAdder();

        //加1
        longAdder.increment();
        longAdder.increment();
        longAdder.increment();

        //加指定值
        longAdder.add(2L);
        longAdder.add(2L);

        //减1
        longAdder.decrement();
        longAdder.decrement();

        //返回当前值,特别注意:在没有并发的情况下返回的准确值,在高并发情况下返回的不能保证准确性(瞬间值)
        System.out.println(longAdder.sum());

        //重置longAdder的值,相当于重新new了一个LongAdder
        longAdder.reset();

        //获取当前value的值并重置
        System.out.println(longAdder.sumThenReset());

        //longValue就是调用的longAdder.sum()方法
        System.out.println(longAdder.longValue());

        //LongAccumulator提供了自定义的函数操作
        LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator() {
            //实现前面的数减第二个数
            @Override
            public long applyAsLong(long left, long right) {
                return left - right;
            }
        }, 100);

        longAccumulator.accumulate(1);//100-1 = 99
        longAccumulator.accumulate(2);//99-2 = 97
        longAccumulator.accumulate(3);//97-3 = 94

        System.out.println(longAccumulator.longValue());
    }
}

注意:sum()方法返回当前值,在没有并发的情况下返回的准确值,在高并发情况下返回的不能保证准确性(瞬间值)

阿里规范有一条:如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数) ,那么为什么要这么定义?

LongAdder性能对比

分别对比AtomicLong、LongAdder、LongAccumulator三者的性能,50个线程,每个线程100W次,总点赞数出来

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * LongAdderCompared
 * 分别对比AtomicLong、LongAdder、LongAccumulator三者的性能
 * 案例:
 * 1、50个线程,每个线程100W次,总点赞数出来
 * 2、统计使用AtomicLong、LongAdder、LongAccumulator三种方式的耗时
 *
 * @author lcry
 * @date 2021/09/25 11:03
 */
public class LongAdderCompared {
    public static void main(String[] args) throws InterruptedException {
        ClickNumberNet clickNumberNet = new ClickNumberNet();
        long startTime;
        long endTime;
        CountDownLatch countDownLatch1 = new CountDownLatch(50);
        CountDownLatch countDownLatch2 = new CountDownLatch(50);
        CountDownLatch countDownLatch3 = new CountDownLatch(50);
        CountDownLatch countDownLatch4 = new CountDownLatch(50);

        //----------clickBySync----------
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * 10000; j++) {
                        clickNumberNet.clickBySync();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickBySync result: " + clickNumberNet.number);

        //----------clickByAtomicLong----------
        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);

        //----------clickByLongAdder----------
        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());

        //----------clickByLongAccumulator----------
        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());
    }
}


/**
 * 点赞
 */
class ClickNumberNet {
    //初始点赞0
    int number = 0;
    AtomicLong atomicLong = new AtomicLong(0);
    LongAdder longAdder = new LongAdder();
    LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 0);

    /**
     * 普通的synchronized同步加锁
     */
    public synchronized void clickBySync() {
        number++;
    }

    /**
     * AtomicLong原子加
     */
    public void clickByAtomicLong() {
        atomicLong.incrementAndGet();
    }

    /**
     * LongAdder加
     */
    public void clickByLongAdder() {
        longAdder.increment();
    }

    /**
     * longAccumulator加
     */
    public void clickByLongAccumulator() {
        longAccumulator.accumulate(1);
    }
}

执行结果:

----costTime: 1917 毫秒	 clickBySync result: 50000000
----costTime: 1159 毫秒	 clickByAtomicLong result: 50000000
----costTime: 140 毫秒	 clickByLongAdder result: 50000000
----costTime: 111 毫秒	 clickByLongAccumulator result: 50000000
LongAdder为什么快?

LongAdder类结构图:LongAdder是Striped64的子类

LongAdder

Striped64:

  • Striped64有几个比较重要的成员函数:

    /** Number of CPUS, to place bound on table size        CPU数量,即cells数组的最大长度 */
    static final int NCPU = Runtime.getRuntime().availableProcessors();
    
    /**
     * Table of cells. When non-null, size is a power of 2.
    cells数组,为2的幂,2,4,8,16.....,方便以后位运算
     */
    transient volatile Cell[] cells;
    
    /**基础value值,当并发较低时,只累加该值主要用于没有竞争的情况,通过CAS更新。
     * 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;
    
    /**创建或者扩容Cells数组时使用的自旋锁变量调整单元格大小(扩容),创建单元格时使用的锁。
     * Spinlock (locked via CAS) used when resizing and/or creating Cells. 
     */
    transient volatile int cellsBusy;
    
  • Striped64中一些变量或者方法的定义:

    base: 类似于AtomicLong中全局的value值。再没有竞争情况下数据直接累加到base上,或者cells扩容时,也需要将数据写入到base上
    collide:表示扩容意向,false一定不会扩容,true可能会扩容
    cellsBusy:初始化cells或者扩容cells需要获取锁,0表示无锁状态,1表示其他线程已经持有了锁
    casCellsBusy:通过CAS操作修改cellsBusy的值,CAS成功代表获取锁,返回true
    NCPU:当前计算机CPU数量,Cell数组扩容时会使用到
    getProbe( ):获取当前线程的hash值
    advanceProbe( ):重置当前线程的hash值
    
  • Cell:是 java.util.concurrent.atomic 下 Striped64 的一个内部类

    abstract class Striped64 extends Number {
        @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);
                }
            }
        }
    }
    ......
    

LongAdder为什么这么快?

一句话概括:

LongAdder在无竞争的情况,跟AtomicLong一样,对同一个base进行操作,当出现竞争关系时则是采用化整为零的做法,从空间换时间,用一个数组cells,将一个value拆分进这个数组cells。多个线程需要同时对value进行操作时候,可以对线程id进行hash得到hash值,再根据hash值映射到这个数组cells的某个下标,再对该下标所对应的值进行自增操作。当所有线程操作完毕,将数组cells的所有值和无竞争值base都加起来作为最终结果。

LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。

sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点。

LongAdder

内部有一个base变量,一个Cell[]数组。base变量:非竞态条件下,直接累加到该变量上

Cell[]数组:竞态条件下,累加个各个线程自己的槽Cell[i]中

总结

  • AtomicLong:
    • 原理:CAS+自旋 + incrementAndGet
    • 使用场景:
      1. 低并发下的全局计算
      2. AtomicLong能保证并发情况下计数的准确性,其内部通过CAS来解决并发安全性的问题。
    • 缺陷:
      1. 高并发后性能急剧下降
      2. AtomicLong的自旋会成为瓶颈,N个线程CAS操作修改线程的值,每次只有一个成功过,其它N - 1失败,失败的不停的自旋直到成功,这样大量失败自旋的情况,一下子cpu就打高了。
  • LongAdder:
    • 原理:CAS+Base+Cell数组分散,空间换时间并分散了热点数据
    • 使用场景:高并发下的全局计算
    • 缺陷:
      1. sum求和后还有计算线程修改结果的话,最后结果不够准确,sum执行时,并没有限制对base和cells的更新(一句要命的话)。所以LongAdder不是强一致性的,它是最终一致性的。

性能对比可以参考下老外的一篇博文: LongAdder vs AtomicLong Performance

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值