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