展开
拓展 关闭
订阅号推广码
GitHub
视频
公告栏 关闭

原子类

  • 简介
1、一度认为原子是不可分割的最小单位,故原子类可以认为其操作都是不可分割
2、对多线程访问同一个变量,我们需要加锁,而锁是比较消耗性能的,JDk1.5之后,新增的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式,这些类同样位于JUC包下的atomic包下,发展到JDk1.8,该包下共有17个类,囊括了原子更新基本类型、原子更新数组、原子更新属性、原子更新引用
3、1.8新增的原子类:DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64
  • 原子类基本类型
1、发展至JDk1.8,基本类型原子类有以下几个:AtomicBoolean、AtomicInteger、AtomicLong、DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder
2、大致可以归为3类
    AtomicBoolean、AtomicInteger、AtomicLong     元老级的原子更新,方法几乎一模一样 
    DoubleAdder、LongAdder     对Double、Long的原子更新性能进行优化提升 
    DoubleAccumulator、LongAccumulator     支持自定义运算
  • 代码案例
public class Demo1 {

    // 定义变量sum为0
    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();
        }
    }
    
}
  • 案例2
public class Demo2 {

    public static void main(String[] args) {
        //输入一个数字,如果比上一个输入的大,则直接返回,如果小,则返回上一个
        LongAccumulator longAccumulator = new LongAccumulator((left, right) ->
                left > right ? left : right, 0L
        );      // 第2个参数是初始值

        longAccumulator.accumulate(3L);
        System.out.println(longAccumulator.get());
        longAccumulator.accumulate(5L);
        System.out.println(longAccumulator.get());
    }

}
  • 原子更新数组类型
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
  • 代码案例
public class AtomicIntegerArrayDemo {

    public static void main(String[] args) {
        // new 1个数组
        int[] arr = new int[]{5, 3, 4};
        // 将数组作为参数传入
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
        // 索引下标1的值加8
        System.out.println(atomicIntegerArray.addAndGet(2, 8));

        // 索引下标0的值作为left,2作为right
        int i = atomicIntegerArray.accumulateAndGet(0, 2, (left, right) ->
                left * right
        );
        System.out.println(i);
    }

}
  • 原子地更新属性
1、原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下4个类进行原子字段更新 
    AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference、AtomicReferenceFieldUpdater
2、使用上述类的时候,必须遵循以下原则 
    字段必须是volatile类型的,在线程之间共享变量时保证立即可见 
    字段的描述类型是与调用者与操作对象字段的关系一致。 也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作
    对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段
    只能是实例变量,不能是类变量,也就是说不能加static关键字
    只能是可修改变量,不能使final变量,因为final的语义就是不可修改 
    对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装 类型(Integer/Long)
    如果要修改包装类型就需要使用AtomicReferenceFieldUpdater
  • 代码案例
public class AtomicLongFieldUpdaterDemo {

    public static void main(String[] args) {
        // 要修改的字段
        AtomicLongFieldUpdater<Student> longFieldUpdater = AtomicLongFieldUpdater.newUpdater(Student.class, "id");
        Student xdclass = new Student(1L, "xdclass");
        // 传入对象,expect表示期待的值,即是要修改的值,update表示修改后的值
        longFieldUpdater.compareAndSet(xdclass, 1L, 100L);
        System.out.println("id="+xdclass.getId());

        // 要修改的字段
        AtomicReferenceFieldUpdater<Student, String> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
        referenceFieldUpdater.compareAndSet(xdclass, "xdclass", "wiggin");
        System.out.println("name="+xdclass.getName());
    }

}

// 实体类
class Student{
    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;
    }
}
  • 原子更新引用
AtomicReference:用于对引用的原子更新
AtomicMarkableReference:带版本戳的原子引用类型,版本戳为boolean类型
AtomicStampedReference:带版本戳的原子引用类型,版本戳为int类型
  • 案例1
public class AtomicReferenceDemo {

    // 将student更新为student1
    public static void main(String[] args) {
        AtomicReference<Student> studentAtomicReference = new AtomicReference<>();
        Student student = new Student(1L, "xdclass");
        Student student1 = new Student(2L, "wiggin");
        studentAtomicReference.set(student);
        studentAtomicReference.compareAndSet(student, student1);
        Student student2 = studentAtomicReference.get();
        System.out.println(student2.getName());
    }

}

// 实体类
class Student{
    private long id;
    private 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;
    }
}
posted @ 2022-05-15 16:39  DogLeftover  阅读(80)  评论(0编辑  收藏  举报