Java并发工具之原子类

1. 什么是原子类?

  • 一个操作是不可分割的,即便是在多线程的情况下也可以保证;
  • 作用和锁类似,保证并发安全,但是原子类的粒度更细,锁是锁住好几行代码,原子类锁的是这个变量;
  • 通常情况了,原子类比锁效率更高,除了高度竞争的情况;
原子类类型
基本类型原子类AtomicInteger、AtomicLong、AtomicBoolean
数组类型原子类AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
引用类型原子类AtomicReference、AtomicStampedReference、AtomicMarkableReference
升级类型原子类AtomicIntegerFieldupdater、AtomicLongFieldupdater、AtomicReferenceFieldupdater
累计类型原子类LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulator

2. 基本类型原子类,以AtomicInteger为例

  • AtomicInteger常用方法:get()、getAndSet(int newValue)、getAndIncrement()、getAndDecrement()、getAndAdd(int delta)、compareAndSet(int expect, int update)。
  • AtomicInteger使用示例:
public class AtomicIntegerDemo1 implements Runnable {
    private static final AtomicInteger atomicInteger = new AtomicInteger();
    public void increAtomic() {
        atomicInteger.getAndIncrement();
    }
    private static volatile int count = 0;
    public void increCount() {
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        AtomicIntegerDemo1 r = new AtomicIntegerDemo1();
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("原子类:" + atomicInteger);
        System.out.println("非原子类:" + count);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            increAtomic();
            increCount();
        }
    }
}

原子类:20000
非原子类:17820

2. Adder累加器

  • 是Java8引入的,比较新的一个类;
  • 高并发下的LongAdder比AtomicLong效率高,不过本质是空间换时间;
  • 竞争激烈的时候,LongAdder把不同线程对应到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发性;
  • 演示高并发场景下,LongAdder比AtomicLong性能好:
public class AtomicLongDemo implements Runnable {

    private static AtomicLong counter = new AtomicLong(0);

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            counter.incrementAndGet();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(20);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            service.submit(new AtomicLongDemo());
        }
        service.shutdown();
        while (!service.isTerminated()){

        }
        long end = System.currentTimeMillis();
        System.out.println(counter.get());
        System.out.println("耗时:" + (end - start));
    }
}
100000000
耗时:1718

public class LongAdderDemo implements Runnable {

    private static LongAdder counter = new LongAdder();

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            counter.increment();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(20);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            service.submit(new LongAdderDemo());
        }
        service.shutdown();
        while (!service.isTerminated()){

        }
        long end = System.currentTimeMillis();
        System.out.println(counter.sum());
        System.out.println("耗时:" + (end - start));
    }
}
100000000
耗时:212
  • 多线程情况下AtomicLong的性能不如LongAdder,AtomicLong每一次加法的结果,都要刷到到主内存和且其他线程要更新到线程工作内存,即每次加法都需要同步,导致很耗费资源;

  • LongAdder每个线程会有自己的计数器,仅仅用来在自己线程内计数,这样一来就不会和其他线程额计数器干扰,线程之间不存在竞争关系,在加法过程中,根本不需要同步,这里也没有一个公共的counter来给所有线程同一计数;

  • LongAdder引入了分段累加的概念,内部有一个base变量和一个Cell[]数组共同参与计数,在竞争不激烈的情况下,直接累加到base变量上,在竞争积累时候,利用Cell[]数据,每个线程分散累加到自己的Cell中,每个线程都是一个独立的计数器,在自加的过程中就提高了效率,其本质就是用空间换时间;

  • sum源码分析:
    在这里插入图片描述
    首先有一个Cell数组,然后定义了一个base,当数组为空的时候,直接返回base,当数组不为空的时候,则遍历这个数组,从base开始逐个累加起来,由于求和时没加锁,所以求和过程中不是线程安全的,结果可能会不精确。

  • LongAdder适合于统计计数求和的场景,而且LongAdder之提供了add方法,而AtomicLong还具有cas方法。

3. Accumulator累加器

public class LongAccumulatorDemo implements Runnable {

    private static LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            longAccumulator.accumulate(i);
        }
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(8);
        LongAccumulatorDemo longAccumulatorDemo = new LongAccumulatorDemo();
        for (int i = 0; i < 10; i++) {
            executorService.submit(longAccumulatorDemo);
        }
        executorService.shutdown();
        while (!executorService.isTerminated()) {

        }
        System.out.println(longAccumulator.getThenReset());
    }
}
450
  • Accumulator累加器适用于大量的并行计算的场景,且不对顺序有要求
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值