1、API方法使用
- 构造方法
AtomicIntegerArray atomicIntegerArray1 = new AtomicIntegerArray(100);
源码:
private final int[] array;
public AtomicIntegerArray(int var1) {this.array = new int[var1];
}
int[] array = {10, 20, 30, 0, 0};
AtomicIntegerArray arr = new AtomicIntegerArray(array);源码:
private final int[] array;
public AtomicIntegerArray(int[] var1) {this.array = (int[])var1.clone();
}
底层就是一个数组。
下面方法使用arr对象进行演示。
int[] array = {10, 20, 30, 0, 0};AtomicIntegerArray arr = new AtomicIntegerArray(array);
- get(int var1): 获取指定下标的数据
int re = arr.get(1);
System.out.println(re); //20
- set(int var1, int var2) 设置指定下标位置的值
- lazySet(int var1, int var2)
var1: 数组的下标
var2: 目标值
arr.set(3,1000);
System.out.println(arr.get(3)); //1000arr.lazySet(4,2000);
System.out.println(arr.get(4)); //2000
- length(): 返回数组的长度
System.out.println(arr.length()); //5
- accumulateAndGet(int var1, int var2, IntBinaryOperator var3) : 设置新值并返回最终结果
//参数1 数组下标
//参数2 新值
//参数3 自定义算法(入参: 原值,新值 。 出参:计算结果)
int i1 = arr.accumulateAndGet(0, 200, (x, y) -> x + y);
System.out.println(i1); //210
System.out.println(arr.get(0)); //210源码:
public final int accumulateAndGet(int var1, int var2, IntBinaryOperator var3) {long var4 = this.checkedByteOffset(var1);int var6;int var7;do {//获取原始值var6 = this.getRaw(var4);var7 = var3.applyAsInt(var6, var2);} while(!this.compareAndSetRaw(var4, var6, var7));return var7;}
- compareAndSet(int var1, int var2, int var3) : 给定预期值 若指定位置的原值=预期值 则设置新值反之不进行设置
- weakCompareAndSet(int var1, int var2, int var3)
//参数1: 数组下标位置
//参数2: 预期值
//参数3: 新值
boolean b = arr.compareAndSet(1, 50, 30);
System.out.println(b); //false
System.out.println(arr.get(1)); //20源码:public final boolean compareAndSet(int var1, int var2, int var3) {return this.compareAndSetRaw(this.checkedByteOffset(var1), var2, var3);}
- decrementAndGet(int var1): 指定位置元素值自减1并且返回最终值
int i = arr.decrementAndGet(1);
System.out.println(i); //19
System.out.println(arr.get(1)); //19
源码:
public final int decrementAndGet(int var1) {return this.getAndAdd(var1, -1) - 1;
}public final int getAndAdd(int var1, int var2) {return unsafe.getAndAddInt(this.array, this.checkedByteOffset(var1), var2);
} 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;
}
- incrementAndGet(int var1): 指定位置元素值自加1并且返回最终值
int i = arr.incrementAndGet(1);
System.out.println(i); //21
System.out.println(arr.get(1)); //21
- getAndAccumulate(int var1, int var2, IntBinaryOperator var3) :设置元素新值并返回原始值
//参数1: 数组下标
//参数2: 新值
//参数3: 函数值计算 ->计算出新值
int andAccumulate = arr.getAndAccumulate(1, 200, (x, y) -> x + y);
System.out.println(andAccumulate); // 20
System.out.println(arr.get(1)); //220
- getAndDecrement(int var1): 指定位置元素自减1 并且返回原始值
int andDecrement = arr.getAndDecrement(1);
System.out.println(andDecrement); //20
System.out.println(arr.get(1)); //19
- getAndIncrement(int var1) :指定位置元素自加1 并且返回原始值
int andDecrement = arr.getAndIncrement(1);
System.out.println(andDecrement); //20
System.out.println(arr.get(1)); //21
- getAndSet(int var1, int var2): 设置新值并返回原始值
int andSet = arr.getAndSet(1, 9000);
System.out.println(andSet); //20
System.out.println(arr.get(1)); //9000
- updateAndGet(int var1, IntUnaryOperator var2) :
int i = arr.updateAndGet(1, x -> x + 100);
System.out.println(i); //120
System.out.println(arr.get(1)); //120
- updateAndGet(int var1, IntUnaryOperator var2):
int i = arr.updateAndGet(1, x -> x + 100);
System.out.println(i); //120
System.out.println(arr.get(1)); //120
2、核心源码
AtomicIntegerArray.java
private static final int base;
private static final int shift;static {base = unsafe.arrayBaseOffset(int[].class);int var0 = unsafe.arrayIndexScale(int[].class);if ((var0 & var0 - 1) != 0) {throw new Error("data type scale not a power of two");} else {shift = 31 - Integer.numberOfLeadingZeros(var0);}
}
base: 对象中int数组的偏移地址
shift: 偏移位数
cas需要三个参数(绝对地址,期望值,新值),根据绝对地址获取值然后与期望值进行比较,若相等则更新为新值,反之不更新。
数组中指定位置元素的绝对地址如何获取 ?
首先需要直到对象的首地址(绝对地址);
其次需要知道数组的首地址(相对地址,相对对象首地址偏移);
知道数组中每个元素占用的字节,元素占用的字节*元素的下标=元素的偏移地址(相对数组首地址偏移);
上述三个地址之和等于指定位置元素的绝对地址。
base = unsafe.arrayBaseOffset(int[].class): 获取数组的首地址;
int var0 = unsafe.arrayIndexScale(int[].class);: 获取数组中单个元素占用的字节大小;
Integer.numberOfLeadingZeros(var0): 计算出前面有多少个0;
int shift = 31 - Integer.numberOfLeadingZeros(var0); 计算出需要左移多少位;
举例: 假设是int[] 单个元素占用4字节, 第5个元素的首地址应该是(前面有4个元素)
4 * 4 = 16;
此时 var0 = 4
Integer.numberOfLeadingZeros(var0) 前面有29个0;
int shift = 31 - Integer.numberOfLeadingZeros(var0); // = 2
第五个元素; 数组下标=4; 4 << 2 = 16; 效率更高。