原子类(一)——AtomicInteger

原子操作

原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。

原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征。

原子类

在Java中提供了很多原子类,主要把这些原子类分成以下几大类
在这里插入图片描述

原子更新基本类型或引用类型

  1. AtomicBoolean
    原子更新布尔类型,内部使用int类型的value存储1和0表示true和false,底层也是对int类型的原子操作。
  2. AtomicInteger
    原子更新int类型。
  3. AtomicLong
    原子更新long类型。
  4. AtomicReference
    原子更新引用类型,通过泛型指定要操作的类。
  5. AtomicMarkableReference
    原子更新引用类型,内部使用Pair承载引用对象及是否被更新过的标记,避免了ABA问题。
  6. AtomicStampedReference
    原子更新引用类型,内部使用Pair承载引用对象及更新的邮戳,避免了ABA问题。

原子更新数组中的元素

原子更新数组中的元素,可以更新数组中指定索引位置的元素。

  1. AtomicIntegerArray
    原子更新int数组中的元素。
  2. AtomicLongArray
    原子更新long数组中的元素。
  3. AtomicReferenceArray
    原子更新object数组中的元素。

原子更新对象中的字段

原子更新对象中的字段,可以更新对象中指定字段名称的字段。

  1. AtomicIntegerFieldUpdater
    原子更新对象中的int类型字段。
  2. AtomicLongFieldUpdater
    原子更新对象中的long类型字段。
  3. AtomicReferenceFieldUpdater
    原子更新对象中的引用类型字段。

高性能原子类

高性能原子类,是Java8中增加的原子类,它们使用分段的思想,把不同线程hash到不同的段上去更新,最后再把这些段的值相加得到最终的值。

  1. Striped64
    下面四个类的父类。
  2. LongAccumulator
    long类型的聚合器,需要传入一个long类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
  3. LongAdder
    long类型聚合器,LongAccumulator的特例,只能用来计算加法,且从零开始计算。
  4. DoubleAccumulator
    double类型的聚合器,需要传入一个double类型的二元操作,可以用来计算各种聚合操作,包括加乘等。
  5. DoubleAdder
    double类型聚合器,DoubleAccumulator的特例,只能用来计算加法,且从零开始计算。

AtomicInteger使用

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {
    public static void main(String[] args) throws InterruptedException {
        test1();
        test2();
    }
    private static void test1() throws InterruptedException {
        Counter counter = new Counter();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    counter.addCount();
                }
            }).start();
        }
        Thread.sleep(1000);
        System.out.println("test1 count = " + counter.getCount());
    }

    private static void test2() throws InterruptedException {
        AtomicInteger count = new AtomicInteger();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    count.incrementAndGet();
                }
            }).start();
        }
        Thread.sleep(1000);
        System.out.println("test2 count = " + count.get());
    }
}

public class Counter {
    private volatile static int count = 0;

    public void addCount(){
        count++;
    }
    public int getCount(){
        return count;
    }
}

运行以上代码会发现,test1的结果达不到预期的10000,而且每次的结果不可再现。而test2的结果每次都是10000,是确定的可再现的。
是因为test1中调用的Counter类的addCount方法,这个方法不是原子性的。count++可以分解为以下几个原子性的步骤:
1.读取count的值
2.计算新值count+1
3.新值写入count变量
如果步骤1、2、3中有多个线程并发执行,那么就会出现两个或多个线程并发的执行+1操作,而我们希望的是每个线程依次执行+1的操作。

AtomicInteger原理

AtomicInteger声明

public class AtomicInteger extends Number implements java.io.Serializable

Unsafe类的使用

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

AtomicInteger属性

private volatile int value;

AtomicInteger构造器

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }

AtomicInteger自增

    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

调用Unsafe类的方法

    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

compareAndSwapInt即CAS方式修改int值:

  1. 调用unsafe.getAndAddInt方法。
  2. unsafe.getAndAddInt方法通过自旋的方式,每次尝试通过CAS方式对原值进行累加。如果累加失败,将进入下一次循环。如果累加成功,则自选结束。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值