返回

深度解析 Java LongAdder 原子加法器的奥秘

后端

序曲:并发编程中的原子性挑战

在多线程编程中,原子性是一项至关重要的原则。它确保共享变量在被多个线程同时访问时,其值总是处于一个一致的状态。原子操作能够保证共享变量在执行过程中不会被中断,从而避免数据的不一致。

然而,在传统的 Java 并发编程中,实现原子性操作往往需要借助于诸如 synchronized 或 Lock 锁之类的同步机制。这些同步机制虽然能够保证原子性,但也带来了性能上的开销。为了解决这一问题,Java 8 引入了 LongAdder 原子类,它是一种无锁并发计数器,能够在无需同步的情况下实现原子性加法操作。

LongAdder 的诞生:无锁并发计数器的崛起

LongAdder 是 Java 8 中引入的并发工具类,它旨在解决传统并发计数器在高并发场景下的性能瓶颈问题。LongAdder 采用了分段锁的设计模式,将计数器划分为多个独立的段,每个段都有自己的锁。当多个线程同时对计数器进行加法操作时,每个线程都可以对不同的段进行加法操作,从而避免了锁竞争的情况。

LongAdder 的源码剖析:揭秘并发性能提升之谜

LongAdder 的源码位于 java.util.concurrent.atomic 包下,让我们来逐行剖析其奥秘。

public class LongAdder extends Striped64 {

    // 省略其他代码

    public void add(long x) {
        Striped64.Cell[] as;
        long b;
        int m;
        UncontendedStriped64 unstriped;

        if ((as = cells) != null || !casBase(b = base, b + x)) {
            boolean wasUncontended = true;
            try {
                int[] hc;
                int h;
                long oldSum;
                int slot;
                Striped64.Cell a;

                // 获取哈希码和槽位
                if ((hc = threadHashCode.get()) == null || (h = sun.misc.Hashing.smear(hc)) == 0) {
                    h = Hashing.randomHashSeed(this);
                    ThreadLocalRandom.current().setRandomSeed(h);
                    hc = threadHashCode.set(new int[]{h});
                }

                if ((as == null || (m = as.length - 1) < 0 || (a = as[slot = (h & m)]) == null) ||
                        !a.cas(oldSum = a.value, oldSum + x)) {
                    wasUncontended = false;

                    // 扩容或重试
                    if (as == cells) {
                        Striped64.tryExpandCapacity(this, h, slot, x);
                    }
                    else if (cells != as) {
                        casCells(as, cells);
                        add(x);
                    }
                }
            }
            finally {
                // 如果是无竞争,则重置哈希码
                if (wasUncontended) {
                    threadHashCode.remove();
                }
            }
        }
    }

    // 省略其他代码

}

从源码中可以看出,LongAdder 采用了分段锁的设计模式,并在 add() 方法中实现了无锁的加法操作。当多个线程同时对计数器进行加法操作时,每个线程都可以对不同的段进行加法操作,从而避免了锁竞争的情况。

LongAdder 的应用场景:并发计数的最佳利器

LongAdder 非常适合用于高并发场景下的计数操作,例如:

  • 统计网站访问量
  • 统计商品销售数量
  • 统计用户在线人数
  • 统计系统请求数

结语:LongAdder 的力量,让并发编程更轻松

LongAdder 作为 Java 8 中引入的并发工具类,通过引入 segment 化策略,显著提升了并发性能,成为高并发场景下计数操作的最佳利器。掌握 LongAdder 的使用,可以帮助您轻松应对复杂多线程环境,让并发编程变得更加轻松高效。