原子类

原子类

1.概念
原子是世界上的最小单位,具有不可分割性,原子类指的是这个类具有原子操作,这个操作不可分割,Java中的原子类在java.util.concurrent.atomic包下,这些类都是使用非阻塞的CAS算法实现的。

具体的测试代码:原子类的操作
2.原子更新基本类型
使用原子的方式更新基本类型,Atomic包有以下几个类:

  • AtomicInteger (原子更新整形)
  • AtomicBoolean(原子更新布尔类型)
  • AtomicLong(原子更新长整形)
    以AtomicInteger为例,它的内部通过UnSafe来实现,具体代码如下:
 public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();//获得Unsafe实例
    private static final long valueOffset;//存放value的偏移量
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    private volatile int value;//实际变量值,用volatile保证了内存的可见性
     public AtomicInteger(int initialValue) {
        value = initialValue;//初始化value值
    }
    ......

这里说明一下Unsafe类
Unsafe类可以直接操作内存,所以在高并发条件下它的效率很高,但是因为可以直接操作内存,就无法通过jvm来回收,必须手动回收,并且Unsafe类会修改内存中对象的地址,使用不当会使程序崩溃。

public final class Unsafe {  //声明这个类为final,表明不可继承
  private static final Unsafe theUnsafe;  //声明thUnSafe引用
  public static final int INVALID_FIELD_OFFSET = -1;
  public static final int ARRAY_BOOLEAN_BASE_OFFSET;//布尔数组的基本偏移量
 ...
  public static final int ARRAY_BOOLEAN_INDEX_SCALE;//布尔数组的比例因子
  ...
   public static final int ADDRESS_SIZE;

    private static native void registerNatives();//注册native方法,用native表明实现不是通过java而是通过c或者c++等其他语言

    private Unsafe() {
    }//构造方法设为私有,则无法直接new获得实例对象

    @CallerSensitive
    public static Unsafe getUnsafe() {  //通过反射机制来获得Unsafe实例
        Class var0 = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }
 }

Unsafe初始化主要通过getUnsafe方法的单例模式实现的,通过Reflection类的getCallerClass判断当前的类是否是有主类加载器加载的,不是的话就抛出异常。

CAS(compare and swap)即比较和交换,CAS是一种无锁算法,它有内存值V,旧的预期值A,新值B,只有当旧的预期值A和内存值V相等,才修改为B的值。

Unsafe中的CAS机制的方法:

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

来看AtomicInteger的几个主要函数:

    public final void set(int newValue) {
        value = newValue;
    }//设置值为给定值
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }//设置值,但是不保证写完之后对其他线程可见
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    } //以原子方式将当前值加一,返回的是旧值
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }//以原子方式将当前值减一,返回的是旧值
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }//以原子方式将当前值加给定值,返回的是旧值
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    } //以原子方式将当前值加一,返回的是加一后的值
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }//以原子方式将当前值减一,返回的是减一后的值
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }//以原子方式将当前值加给定值,返回的是改变后的值

3.原子更新数组

  • AtomicIntegerArray (原子更新整型数组里的元素)
  • AtomicReferenceArray (原子更新引用类型数组里的元素)
  • AtomicLongArray(原子更新长整型数组里的元素)

以AtomicReferenceArray为例:

public class AtomicReferenceArray<E> implements java.io.Serializable {
    private static final long serialVersionUID = -6209656149925076980L;
    private static final Unsafe unsafe; //声明unsafe引用
    private static final int base; //基础地址
    private static final int shift;  //偏移量
    private static final long arrayFieldOffset; //array数组的偏移量
    private final Object[] array;  //对象数组

    static {
        try {
            unsafe = Unsafe.getUnsafe();
            arrayFieldOffset = unsafe.objectFieldOffset
                (AtomicReferenceArray.class.getDeclaredField("array"));
            base = unsafe.arrayBaseOffset(Object[].class);
            int scale = unsafe.arrayIndexScale(Object[].class);//获取数组元素占数组的大小
            if ((scale & (scale - 1)) != 0)//判断其是否为2的幂次方
                throw new Error("data type scale not a power of two");
            shift = 31 - Integer.numberOfLeadingZeros(scale);
        } catch (Exception e) {
            throw new Error(e);
        }
    }
    private long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length)
            throw new IndexOutOfBoundsException("index " + i);
        return byteOffset(i);
    }//先检查索引是否超出数组,是就抛出异常,不是的话查找下标为i的偏移量
    private static long byteOffset(int i) {
        return ((long) i << shift) + base;
    }//查找下标为i的偏移量
    public AtomicReferenceArray(int length) {
        array = new Object[length];
    }//构造方法,以指定长度实例化数组
    public AtomicReferenceArray(E[] array) {
        // Visibility guaranteed by final field guarantees
        this.array = Arrays.copyOf(array, array.length, Object[].class);
    }
}

常用方法:

public final E get(int i) {
        return getRaw(checkedByteOffset(i));
}//得到下标为i的元素
public final void set(int i, E newValue) {
        unsafe.putObjectVolatile(array, checkedByteOffset(i), newValue);
}//设置下标为i为给定值
public final E getAndSet(int i, E newValue) {
        return (E)unsafe.getAndSetObject(array, checkedByteOffset(i), newValue);
}//设置下标为i为给定值,并且返回旧值
public final boolean compareAndSet(int i, E expect, E update) {
        return compareAndSetRaw(checkedByteOffset(i), expect, update);
}//如果下标为i的值与期望值相同,就更新值,返回true,否则返回false

4.原子更新引用类型

  • AtomicReference (原子更新引用类型)
  • AtomicReferenceFieldUpdater(原子更新引用类型的字段)
  • AtomicMarkableReferce(原子更新带有标记位的引用类型)

这三个类和AtomicInteger等类似,只是数据类型换成引用数据类型。

5.原子更新字段

  • AtomicIntegerFieldUpdater(原子更新整型的字段的更新器)
  • AtomicLongFieldUpdater(原子更新长整型的字段的更新器)
  • AtomicStampedReference(原子更新带有版本号的引用类型)
  • AtomicReferenceFieldUpdater(原子更新引用类型的字段的更新器)

除了AtomicStampedReference,其他三个都为抽象类,所以它们需要使用newUpdater方法创建更新器,并且更新类的字段时需要使用volatile关键字修饰。

6.jdk1.8出现的操作类

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

以LongAdder为例,它与普通的AtomicLong的区别主要体现在在高并发时对单一变量的操作转换成cells数组中多个元素的CAS操作,取值时进行求和。在低并发的情况下与AtomicLong原理相同。

使用场景:普通变量在高并发情况下,进行加减操作可能非线程安全的,可以通过原子类的操作保证线程安全,它比使用锁性能上更好。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值