原子类

1、什么是原子类

  • 一度认为原子是不可分割的最小单位,故原子类可以认为其操作都是不可分割
  • 为什么要有原子类?
    对多线程访问同一个变量,我们需要加锁,而锁是比较消耗性能的,JDk1.5之后,新增的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式,这些类同样位于JUC包下的atomic包下,发展到JDk1.8,该包下共有17个类,囊括了原子更新基本类型、原子更新数组、原子更新属性、原子更新引用
  • 1.8新增的原子类
    DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64

2、原子更新基本类型

  • 发展至JDk1.8,基本类型原子类有以下几个:
    AtomicBoolean、AtomicInteger、AtomicLong、DoubleAccumulator、DoubleAdder、
    LongAccumulator、LongAdder
public class AtomicIntegerDemo {
    //整个内部的操作时原子操作
    private static AtomicInteger sum = new AtomicInteger(0);
    public static void inCreate(){
        sum.incrementAndGet();
    }

    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            new Thread(()->{
                for(int j =0 ;j<100;j++){
                    inCreate();
                    System.out.println(sum);
                }
            }).start();
        }
    }
}

在这里插入图片描述

public class LongAccumulatorDemo {
    public static void main(String[] args) {
        //输入一个数字,如果比上一个输入的大,则直接返回;如果小,则返回上一个
        //0L是默认值
//        LongAccumulator longAccumulator = new LongAccumulator((left, right) ->
//                left > right ? left : right,0L
//        );
        LongAccumulator longAccumulator = new LongAccumulator((left, right) ->
                left * right,2L
        );
        //输入一个参数。会去调用上面的方法
        longAccumulator.accumulate(3L);
        //输出运算之后的一个结果
        System.out.println(longAccumulator.get());
        longAccumulator.accumulate(5L);
        System.out.println(longAccumulator.get());
        longAccumulator.accumulate(4L);
        System.out.println(longAccumulator.get());
    }
}

在这里插入图片描述

  • 大致可以归为3类
    AtomicBoolean、AtomicInteger、AtomicLong 元老级的原子更新,方法几乎一模一样
    DoubleAdder、LongAdder 对Double、Long的原子更新性能进行优化提升
    DoubleAccumulator、LongAccumulator 支持自定义运算

3、原子更新数组类型

AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

public class AtomicIntegerArrayDemo {
    public static void main(String[] args) {
        int[] arr = new int[]{3,2};
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
        //数组中的第二个值加5并返回
        int result = atomicIntegerArray.addAndGet(1,5);
        System.out.println(result);

        //自定义计算
        int result2 =atomicIntegerArray.accumulateAndGet(0,2,(left, right) ->
                left * right /2
                );
        System.out.println(result2);
    }
}

在这里插入图片描述

4、原子地更新属性

原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下4个类进行原子字段更新
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference、AtomicReferenceFieldUpdater

public class AtomicLongFieldUpdaterDemo {
    public static void main(String[] args) {
        AtomicLongFieldUpdater<Student> longFieldUpdater = AtomicLongFieldUpdater.newUpdater(Student.class, "id");
        Student student = new Student(1L,"fbiao");
        longFieldUpdater.compareAndSet(student,1L,100L);
        System.out.println("id="+student.getId());

        AtomicReferenceFieldUpdater<Student, String> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
        referenceFieldUpdater.compareAndSet(student,"fbiao","fbiao2");
        System.out.println("name="+student.getName());
    }
}
class Student{
    //要求进行原子更新的类必须由volatile关键字修饰
    volatile long id;
    volatile String name;
    public Student(Long id,String name){
        this.id=id;
        this.name=name;
    }
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

在这里插入图片描述
使用上述类的时候,必须遵循以下原则
字段必须是volatile类型的,在线程之间共享变量时保证立即可见
字段的描述类型是与调用者与操作对象字段的关系一致。
也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。
对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
只能是实例变量,不能是类变量,也就是说不能加static关键字。
只能是可修改变量,不能使final变量,因为final的语义就是不可修改。
对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。
如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。

5、原子更新引用

场景:现在有两个类,以第二个类里面所有的内容去替换第一个类。
AtomicReference:用于对引用的原子更新
AtomicMarkableReference:带版本戳的原子引用类型,返回版本戳为boolean类型。
AtomicStampedReference:带版本戳的原子引用类型,返回版本戳为int类型。

/**
 * 现在有两个类,以第二个类里面所有的内容去替换第一个类
 */
public class AtomicReferenceDemo {
    public static void main(String[] args) {
        AtomicReference<Studentt> studentAtomicReference = new AtomicReference<>();
        Studentt student1 = new Studentt(1L,"fbiao1");
        Studentt student2 = new Studentt(2L,"fbiao2");

        //设置将要被替换的对象
        studentAtomicReference.set(student1);
        //以student2替换掉student1的内容
        studentAtomicReference.compareAndSet(student1,student2);
        //获取替换后的对象
        Studentt student3 = studentAtomicReference.get();
        System.out.println(student3.getId()+","+student3.getName());
    }

}

class Studentt {
    private long id;
    private String name;

    public Studentt(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值