现在的位置: 首页 > 综合 > 正文

AtomicLong是什么?能解决什么问题

2020年02月19日 综合 ⁄ 共 2532字 ⁄ 字号 评论关闭

  相信你一定记得学习并发编程的一个入门级例子,多个线程操作一个变量,累加 10000 次,最后结果居然不是 10000。后来你把这个变量换成了并发包中的原子类型变量 AtomicLong,完美的解决了并发问题。假如面试官问:还有更好的选择吗?LongAdder 了解过吗?你能对答如流吗?

  AtomicLong由来

  AtomicLong 是 Java 1.5 并发包中提供的一个原子类,他提供给了我们在多线程环境下安全的并发操作一个整数的特性。并且性能还可以,它主要依赖了 2 个技术,volatile 关键字和 CAS 原子指令,AtomicLong 性能已经不错了,但是当在线程高度竞争的状况下性能会急剧下降,因为高度竞争下 CAS 操作会耗费大量的失败计算,因为当一个线程去更新变量时候发现值早已经被其他线程更新了。那么有没有更好的解决方案呢,于是 LongAdder 诞生了。

  LongAdder 是 Java 1.8 并发包中提供的一个工具类,它采用了一个分散热点数据的思路。简单来说,Atomic 中所有线程都去更新内存中的一个变量,而 LongAdder 中会有一个 Cell 类型的数组,这个数组的长度是 CPU 的核心数,因为一台电脑中最多同时会有 CPU 核心数个线程并行运行,每个线程更新数据时候会被映射到一个 Cell 元素去更新,这样就将原本一个热点的数据,分散成了多个数据,降低了热点,这样也就减少了线程的竞争程度,同时也就提高了更新的效率。当然这样也带了一个问题,就是更新函数不会返回更新后的值,而 AtomicLong 的更新方法会返回更新后的结果,LongAdder 只有在调用 sum 方法的时候才会去累加每个 Cell 中的数据,然后返回结果。当然 LongAdder 中也用到了volatile 和 CAS 原子操作,所以小伙伴们一定要掌握这俩个技术点,这是面试必问的点。

  既然说 LongAdder 的效率更好,那我们就来一段测试代码,展示一下 LongAdder 的腻害之处,请看如下:

  public class AtomicLongTester {

  private static AtomicLong numA = new AtomicLong(0);

  private static LongAdder numB = new LongAdder();

  public static void main(String[] args) throws InterruptedException {

  for (int i = 1; i < 10001; i*=10) {

  test(false, i);

  test(true, i);

  }

  }

  public static void test(boolean isLongAdder, int threadCount) throws InterruptedException {

  long starTime = System.currentTimeMillis();

  final CountDownLatch latch = new CountDownLatch(threadCount);

  for (int i = 0; i < threadCount; i++) {

  new Thread(new Runnable() {

  public void run() {

  for (int i = 0; i < 100000; i++) {

  if (isLongAdder) {

  numB.add(1);

  } else {

  numA.addAndGet(1);

  }

  }

  latch.countDown();

  }

  }).start();

  }

  // 等待所有运算结束

  latch.await();

  if (isLongAdder) {

  System.out.println("Thread Count=" + threadCount + ", LongAdder cost ms=" + (System.currentTimeMillis() - starTime) + ", Result=" + numB.sm());

  } else

  System.out.printn("Thread Count=" + threadCount + ", AtomicLong cost ms=" + (System.currentTimeMillis() - starTime) + ", Result=" + numA.get());

  }

  numA = new AtomicLong(0);

  numB = new LongAdder();

  }

  }

  实验结果大致如下:

  Thread Count=1, AtomicLong cost ms=9, Result=100000

  Thread Count=1, LongAdder cost ms=13, Result=100000

  Thread Count=10, AtomicLong cost ms=14, Result=1000000

  Thread Count=10, LongAdder cost ms=41, Result=1000000

  Thread Count=100, AtomicLong cost ms=111, Result=10000000

  Thread Count=100, LongAdder cost ms=45, Result=10000000

  Thread Count=1000, AtomicLong cost ms=1456, Result=100000000

  Thread Count=1000, LongAdder cost ms=379, Result=100000000

  Thread Count=10000, AtomicLong cost ms=17452, Result=1000000000

  Thread Count=10000, LongAdder cost ms=3545, Result=1000000000

  从上面的结果可以看出来,当线程竞争率比较低的时候 AtomicLong 效率还是优于 LongAdder 的,但是当线程竞争率增大的时候,我们可以看出来 LongAdder 的性能远远高于 AtomicLong。

  因此在使用原子类的时候,我们要结合实际情况,如果竞争率很高,那么建议使用 LongAdder 来替代 AtomicLong。

抱歉!评论已关闭.