原子类之原子数组

一、数组类型的原子类

原子数组类型,这个其实和AtomicInteger等类似,只不过在修改时需要指明数组下标。

CAS是按照==来根据地址进行比较。数组比较地址,肯定是不行的,只能比较下标元素。而比较下标元素,就和元素的类型有关系了。

java.util.concurrent.atomic中,原子类型数组有以下四种:

类名 说明
AtomicIntegerArray 提供对int[]数组元素的原子性更新操作
AtomicLongArray 提供对long[]数组元素的原子性更新操作
AtomicReferenceArray 提供对引用类型[]数组元素的原子性更新操作
AtomicBooleanArray 原子更新布尔类型数组的元素

使用原子的方式更新数组里的某个元素。

二、常用方法

其中AtomicIntegerArrayAtomicLongArray的使用方式差别不大,AtomicReferenceArray因为他的参数为引用数组,所以跟前两个的使用方式有所不同。AtomicBooleanArray在生产中使用的很少。

本次只对AtomicLongArrayAtomicReferenceArray方法进行详细的介绍。

2.1 AtomicLongArray介绍

构造方法
方法名 说明
AtomicLongArray(int length) 创建给定长度的新 AtomicLongArray
AtomicLongArray(long[] array) 创建与给定数组具有相同长度的新 AtomicLongArray,并从给定数组复制其所有元素

源码

1
2
3
4
5
6
7
8
9
10
// 实例化一个AtomicLongArray,设置数组大小
public AtomicLongArray(int length) {
array = new long[length];
}

//创建一个新的AtomicLongArray,并给定一个数组初始化
public AtomicLongArray(long[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}
方法
方法 说明
long getAndIncrement(int i) 以原子方式将索引 i 的元素自增 1,并返回旧值
long incrementAndGet(int i) 以原子方式将索引 i 的元素自增 1,并返回减少之后的值
long getAndDecrement(int i) 以原子形式将索引i处的元素原子自减1,并返回旧值
long decrementAndGet(int i) 以原子形式将索引i处的元素原子自减1,并返回减少之后的值
long addAndGet(int i, long delta) 以原子形式将给定元素与数组中索引i的元素相加
long getAndSet(int i, long newValue) 将地位i处的元素原子设置为给定值,并返回旧值
long getAndIncrement(int i) 原子的将给定的值增加到索引i的元素
long get(int i) 获取位置 i 的当前值
void lazySet(int i, long newValue) 最终将位置 i 的元素设置为给定值
int length() 返回数组的长度
void set(int i, long newValue) 将位置 i 的元素设置为给定值
boolean compareAndSet(int i,int expect,int update) 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
boolean weakCompareAndSet(int i, int expect, long update) 如果当前值 == 预期值,则以原子方式将位置 i 的元素设置为给定的更新值
long getAndUpdate(int i, LongUnaryOperator updateFunction) 应用将给定函数利用以后值和给定值的原子更新以后值,返回旧值
long updateAndGet(int i, LongUnaryOperator updateFunction) 应用将给定函数利用以后值和给定值的原子更新以后值,返回新值
方法示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;

/**
* @author xiaowei
* @date 2022-11-07
* @description AtomicLongArray示例
**/
public class AtomicExample {
/**
* 初始化 数组长度为 10
*/
private static AtomicLongArray arr = new AtomicLongArray(5);

private static LongUnaryOperator longUnaryOperator = new LongUnaryOperator() {
@Override
public long applyAsLong(long operand) {
// 以后索引 + 10
return operand + 10;
}

};

private static LongBinaryOperator accumulatorFunction = new LongBinaryOperator() {

@Override
public long applyAsLong(long left, long right) {
return left + right;
}
};

public static void main(String[] args) {
for (int i = 0; i < arr.length(); i++) {
System.out.println("i-" + i + "=" + arr.get(i));
}

// 以原子形式给以后索引下标为(0)值加1,返回新值 (i++): 0
System.out.println("索引 0 incrementAndGet=" + arr.getAndIncrement(0));

// 以原子形式给以后索引下标为(0)值加1,,返回新值(++i) 两次减少 : 2
System.out.println("索引 0 incrementAndGet=" + arr.incrementAndGet(0));

//以原子形式给以后索引下标为(0)值缩小 1,返回旧值 (i--):2
System.out.println("索引 0 incrementAndGet=" + arr.getAndDecrement(0));

//以原子形式给以后索引下标为(0)值缩小 1,返回旧值 (--i):0
System.out.println("索引 0 incrementAndGet=" + arr.decrementAndGet(0));

// 以原子形式将输出的数值与实例中的值(AtomicLongArray(0)里的value)相加,并返回后果 : 100
System.out.println("索引 0 addAndGet=" + arr.addAndGet(0, 100));

// 获取 AtomicLongArray 的 value 100
System.out.println("索引 0 get=" + arr.get(0));

System.out.println("*********** JDK 1.8 ***********");

//应用将给定函数利用给以后值和给定值的后果原子更新以后值,返回上一个值
// 索引下标为 0 执行指定函数 后果为 100 + 10
System.out.println("索引 0 getAndUpdate=" + arr.updateAndGet(0, longUnaryOperator));

// 索引下标为 1 执行指定函数 后果为 0 + 10
System.out.println("索引 1 getAndUpdate=" + arr.updateAndGet(1, longUnaryOperator));

// 应用给定函数利用给指定下标和给定值的后果原子更新以后值,并返回后果 20
System.out.println("索引 1 accumulateAndGet=" + arr.accumulateAndGet(1, 10, accumulatorFunction));
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
i-0=0
i-1=0
i-2=0
i-3=0
i-4=0
索引 0 incrementAndGet=0
索引 0 incrementAndGet=2
索引 0 incrementAndGet=2
索引 0 incrementAndGet=0
索引 0 addAndGet=100
索引 0 get=100
*********** JDK 1.8 ***********
索引 0 getAndUpdate=110
索引 1 getAndUpdate=10
索引 1 accumulateAndGet=20

2.2 AtomicReferenceArray介绍

构造方法
方法名 说明
AtomicReferenceArray(E[] array) 创建与给定数组具有相同长度的新 AtomicReferenceArray,并从给定数组复制其所有元素
AtomicReferenceArray(int length) 创建给定长度的新 AtomicReferenceArray

源码

1
2
3
4
5
6
7
8
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);
}
方法
方法 说明
boolean compareAndSet(int i, E expect, E update) 如果当前值 == 预期值,则以原子方式将位置 i 的元素设置为给定的更新值
E get(int i) 获取位置 i 的当前值
E getAndSet(int i, E newValue) 以原子方式将位置 i 的元素设置为给定值,并返回旧值
void lazySet(int i, E newValue) 最终将位置 i 的元素设置为给定值
int length() 返回该数组的长度
void set(int i, E newValue) 将位置 i 的元素设置为给定值
boolean weakCompareAndSet(int i, E expect, E update) 如果当前值 == 预期值,则以原子方式将位置 i 的元素设置为给定的更新值
方法示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.concurrent.atomic.AtomicReferenceArray;

/**
* @author xiaowei
* @date 2022-11-07
* @description AtomicReferenceArray示例
**/
public class AtomicReferenceArrayTest {
public static void main(String[] args) {
Long[] l = new Long[4];
String[] s = new String[4];
int[] i = new int[4];
Integer[] in = new Integer[4];
AtomicReferenceArray atomicReferenceArray = new AtomicReferenceArray(l);
System.out.println(atomicReferenceArray.length());
System.out.println(atomicReferenceArray.get(2));

AtomicReferenceArray atomic = new AtomicReferenceArray(4);
atomic.set(0,12);
atomic.set(2,"Leefs");
atomic.set(3,i);
System.out.println(atomic.toString());
}
}

运行结果

1
2
3
4
null
[12, null, Leefs, [I@63947c6b]

说明

  • 当使用AtomicReferenceArray(E[] array)这个构造方法传入一个数组对象时,该数组对象必须是引用类型,int[]不可以,但是Integer[]的可以。
  • 当使用AtomicReferenceArray(int length)这个构造函数的时候,只要为他指定了数组大小之后,为数组的每一位设置什么值是没有要求的,类似于Map的形式。

三、原子性测试

创建10个线程,每个线程分别对数组操作(自增)10000次(采用了函数式编程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
参数1:提供数组、可以是线程不安全数组或线程安全数组
参数2:获取数组长度的方法
参数3:自增方法,回传 array, index
参数4:打印数组的方法
*/
// supplier 提供者 无中生有 ()->结果
// function 函数 一个参数一个结果 (参数)->结果, BiFunction (参数1,参数2)->结果
// consumer 消费者 一个参数没结果 (参数)->void, BiConsumer (参数1,参数2)->void
private static <T> void demo(
Supplier<T> arraySupplier,
Function<T,Integer> lengthFun,
BiConsumer<T,Integer> putConsumer,
Consumer<T> printConsumer){
List<Thread> threadList = new ArrayList<>();
//得到数组返回的元素
T array = arraySupplier.get();
//后面的Integer是返回结果
int length = lengthFun.apply(array);
//根据数组长度来进行遍历,对值进行累加
for (int i = 0; i < length; i++) {
// 每个线程对数组作 10000 次操作
threadList.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
putConsumer.accept(array, j%length);
}
}));
}
threadList.forEach(t -> t.start()); // 启动所有线程
threadList.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 等所有线程结束
printConsumer.accept(array);
}

不安全的数组

1
2
3
4
5
6
demo(
()->new int[10],
(array)->array.length,
(array, index) -> array[index]++,
array-> System.out.println(Arrays.toString(array))
);

运行结果

1
[6794, 6687, 6664, 6587, 6576, 6491, 6480, 6530, 6536, 6740]

安全的数组

1
2
3
4
5
6
demo(
()->new AtomicIntegerArray(10),
(arr)-> arr.length(),
(arr,index) -> arr.getAndIncrement(index),
(arr)->System.out.println(arr)
);

运行结果

1
[10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000]

原子类之原子数组
http://example.com/2023/03/29/原子类之原子数组/
作者
程序员小魏
发布于
2023年3月29日
许可协议