原子类

内容纲要

原子类简介

  • 不可分割
  • 一个操作是不可中断的, 即便是多线程的情况下也可以保证
  • java.util.concurrent.atomic
  • 原子类的作用和锁类似, 是为了保证并发情况下线程安全。不过原子类相比锁,有一定的优势
  • 颗粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得最细颗粒度的情况了,通常锁的颗粒度要大于原子变量的粒度
  • 效率更高:通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况

Atomic*基本类型原子

  • AtomicInteger: 整形原子类
  • AtomicLong: 长整型原子类
  • AtomicBoolean :布尔型原子类

AtomicInteger常用方法

  • public finalint get() //获取当前的值
  • public finalint getAndSet(int newValue)//获取当前的值并设置新的值imooc
  • public finalint getAndIncrement()//获取当前的值,并自增
  • public finalint getAndDecrement() //获取当前的值,并自减
  • public finalint getAndAdd(int delta) //获取当前的值,并加上预期的值
  • boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值( update )

Atomic*Reference引用类型原子类

  • AtomicReference:AtomicReference类的作用,和AtomicInteger并没有本质区别,AtomicInteger可以让一个整数保证原子性,而AtomicReference可以让一个对象保证原子性,当然,AtomicReference的功能明显比AtomicInteger强,因为一个对象里可以包含很多属性。用法和AtomicInteger类似。

把普通变量升级为具有原子功能

  • AtomicIntegerFieldUpdater对普通变量进行升级
  • 使用场景: 偶尔需要一个原子get-set操作
public class AtomicIntegerFieldUpdaterDemo implements Runnable{

    static Candidate tom;
    static Candidate peter;

    public static final AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class,"score");

    @Override
    public void run() {
        for (int i = 0; i < 100000; i++) {
            peter.score ++;
            scoreUpdater.getAndIncrement(tom);
        }
    }

    public static class Candidate{
       volatile int score =0 ;
    }

    public static void main(String[] args) throws InterruptedException {
        tom = new Candidate();
        peter = new Candidate();
        AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
        Thread thread = new Thread(r);
        thread.start();
        Thread thread1 = new Thread(r);
        thread1.start();
        thread.join();
        thread1.join();
        System.out.println("tom:"+tom.score);
        System.out.println("peter:"+peter.score);
    }
}

注意点

  • 可见范围
  • 底层使用反射
  • 不支持static修饰的变量

Adder累加器

  • 是Java8引入的,相对是比较新的一个类
  • 高并发下LongAdder比AtomicLong效率高,不过本质是空间换时间
  • 竞争激烈的时候,LongAdder把不同线程对应到不同的Cell 上进行修改,降低了冲突的概率,是多段锁(ConcurrentHashMap在1.7中也是使用多段锁,1.8改为node) 的理念,提高了并发性

AtomicLong 弊端

LongAdder带来的改进和原理

  • 在内部,这个LongAdder的实现原理和刚才的AtomicLong是有不同的,刚才的AtomicLong的实现原理是,每一次加法都需要做同步,所以在高并发的时候会导致冲突比较多,也就降低了效率
  • 而此时的LongAdder,每个线程会有自己的一个计数器,仅用来在自己线程内计数,这样一来就不会和其他线程的计数器干扰
  • 如上图中所示,第一个线程的计数器数值,也就是ctr,为1的他们之时候,可能线程2的计数器ctr’’的数值已经是3了,间并不存在竞争关系,所以在加和的过程中,根本不需要同步机制,也不需要刚才的flush和refresh。这里也没有一个公共的counter来给所有线程统一计数
  • LongAdder引入了分段累加的概念,内部有一个base变量和个CelII数组共同参与计数
  • base变量:竞争不激烈,直接累加到该变量上
  • CellII数组:竞争激烈,各个线程分散累加到自己的槽Cellril中
  • sum 源码
public long sum() {
    Striped64.Cell[] cs = this.cells;
    long sum = this.base;
    if (cs != null) {
        Striped64.Cell[] var4 = cs;
        int var5 = cs.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            Striped64.Cell c = var4[var6];
            if (c != null) {
                sum += c.value;
            }
        }
    }

    return sum;
}

对比AtomicLong和LongAdder

  • 在低争用下,AtomicLong和LongAdder这两个类具有相似的特征。但是在竞争激烈的情况下,LongAdder的预期吞吐量要高得多,但要消耗更多的空间
  • LongAdder适合的场景是统计求和计数的场景,而且LongAdder基本只提供了add方法,而AtomicLong还具有cas方法

Accumulator累加器

  • Accumulator和Adder非常相似,Accumulator就是一个更通用版本的Adder
THE END
分享
二维码
< <上一篇
下一篇>>