AtomicInteger&LongAdder

AtomicInteger&LongAdder

实例

public class MyAtomic {

    public static void main(String[] args) {
        AtomicInteger num = new AtomicInteger(1);
        new Thread(()->AtomicIncrement(num)).start();
        new Thread(()->AtomicIncrement(num)).start();
        new Thread(()->AtomicIncrement(num)).start();
        new Thread(()->AtomicIncrement(num)).start();
        new Thread(()->AtomicIncrement(num)).start();
        //线程不安全
        int count = 1;
        new Thread(()->increment(count)).start();
        new Thread(()->increment(count)).start();
        new Thread(()->increment(count)).start();
        new Thread(()->increment(count)).start();
        new Thread(()->increment(count)).start();
    }

    public static void AtomicIncrement(AtomicInteger num){

        num.addAndGet(1);
        System.out.println("atomicIncrement"+num.intValue());
    }

    public static void increment(int num){
        num++;
        System.out.println("increment"+num);
    }
}
  • 运行结果
    • 可以看出使用AtomicInteger是线程安全的,而使用普通的Integer是线程不安全的
atomicIncrement2
atomicIncrement3
atomicIncrement4
increment2
atomicIncrement5
increment2
increment2
increment2
atomicIncrement6
increment2

AtomicInteger源码分析

//使用volatile保证值在线程之间的可见性
private volatile int value;

public AtomicInteger(int initialValue) {
    value = initialValue;
}

//开始以为会用锁之类的东西
//看源码之后发现就是使用了CAS操作
public final int addAndGet(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}

//使用自旋锁+CAS保证原子性
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}
  • CAS相对于其他锁,不会进行内核态操作,有着一些性能的提升。但同时引入自旋,当锁竞争较大的时候,自旋次数会增加,cpu资源消耗较大。
  • 所以CAS+自旋适用于低并发场景

jdk8做出的优化

  • 在jdk8中引入了4个新的计数器类型,LongAdder、LongAccumulator、DoubleAdder、DoubleAccumulator。他们都是继承自Striped64
  • 那么LongAdder和AtomicLong有什么区别呢?Atomic系列的缺点就是比较适用于低并发场景。所以LongAdder在此基础上引入分段锁的概念。
  • 分段锁,在竞争不激烈的时候,所有线程都是通过CAS对同一个变量进行修改,当竞争激烈的时候,会根据当前线程哈希到对应的Cell进行修改,在每个Cell上上锁。

LongAdder

    LongAdder sum = new LongAdder();
    new Thread(()->LongAdderIncrement(sum)).start();
    new Thread(()->LongAdderIncrement(sum)).start();
    new Thread(()->LongAdderIncrement(sum)).start();
    new Thread(()->LongAdderIncrement(sum)).start();
    new Thread(()->LongAdderIncrement(sum)).start();
    
    public void LongAdderIncrement(LongAdder longAdder){
        longAdder.add(1);
        System.out.println("longadder"+longAdder.intValue());
    }
  • 结果如下
longadder1
longadder2
longadder3
longadder4
longadder5
源码分析
  • Striped64变量分析
    //分离的数据
    transient volatile Cell[] cells;

    //在并发量不大,没有导致CAS失败的情况下,会直接修改该字读
    transient volatile long base;

    //为1时说明线程正在修改cells的大小
    transient volatile int cellsBusy;
  • LongAdder的add方法
public void add(long x) {
    Cell[] as; long b, v; int m; Cell a;
    //如果cells数组不为null或者cells数组为空但是通过cas修改base值失败了
    //cells数组不为空,那么代表曾经发生过并发修改失败了
    //casBase失败说明本次并发修改失败了
    if ((as = cells) != null || !casBase(b = base, b + x)) {
        boolean uncontended = true;
        //如果cells数组为空,直接进入longAccumulate
        //如果cells数组无数据
        //如果cells当前slot的值为null
        //cas当前slot的值失败了
        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);
    }
}
  • Striped64的longAccumulate方法
final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
    int h;
    //getProbe方法是当前线程的随机数!使用这个就能随机的修改cells的值了
    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) {
            //进入这里代表cells不为null
            //随机选择一个slot,如果是空的,则添加一个新的cell添加进去
            if ((a = as[(n - 1) & h]) == null) {
                //0代表cells大小目前没发生变化
                if (cellsBusy == 0) {       // Try to attach new Cell
                    Cell r = new Cell(x);   // Optimistically create
                    //设置cellsBusy=1
                    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;
            }
            //进入这个条件,说明发生锁竞争,并且当前的slot已经被占用了
            //wsasUncontended代表选择下一个随机值,再来一次
            else if (!wasUncontended)       // CAS already known to fail
                wasUncontended = true;      // Continue after rehash
            //如果两个随机值都被占用了,那么就尝试设置下新增当前的slot的值
            else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                         fn.applyAsLong(v, x))))
                break;
            //如果还失败了,那么在判断下cells的个数能否添加了
            //cells个数最大是NCPU的最近的2的倍数
            //如果cells的数目已经>=这个值,那么只会一直改变slot
            //如果slot都被占用的话,就一直尝试去加在这个slot的值上
            else if (n >= NCPU || cells != as)
                collide = false;            // At max size or stale
            else if (!collide)
                collide = true;
            //如果cells的值不太够,小于NCPU的话会扩容cells
            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
            }
            //上面的每次循环结束的时候都会改变下Probe
            //这里就实现了随机的修改cells的某个元素
            h = advanceProbe(h);
        }
        //这里执行cell的初始化,首先检查cellsBusy
        else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
            boolean init = false;
            try {                           // Initialize table
                if (cells == as) {
                    //默认的Cells大小为2
                    Cell[] rs = new Cell[2];
                    rs[h & 1] = new Cell(x);
                    cells = rs;
                    init = true;
                }
            } finally {
                cellsBusy = 0;
            }
            //初始化完毕,直接退出
            if (init)
                break;
        }
        //如果队列为空,并且有其他线程正在实例化Cells的话
        //就去修改base的值
        else if (casBase(v = base, ((fn == null) ? v + x :
                                    fn.applyAsLong(v, x))))
            break;                          // Fall back on using base
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值