李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
35.并发编程之原子数组
Leefs
2022-11-07 PM
1139℃
0条
[TOC] ### 一、数组类型的原子类 原子数组类型,这个其实和`AtomicInteger`等类似,只不过在修改时需要指明数组下标。 CAS是按照`==`来根据地址进行比较。数组比较地址,肯定是不行的,只能比较下标元素。而比较下标元素,就和元素的类型有关系了。 **在`java.util.concurrent.atomic`中,原子类型数组有以下四种:** | 类名 | 说明 | | ---------------------- | ------------------------------------------ | | `AtomicIntegerArray` | 提供对int[]数组元素的原子性更新操作 | | `AtomicLongArray` | 提供对long[]数组元素的原子性更新操作 | | `AtomicReferenceArray` | 提供对`引用类型[]`数组元素的原子性更新操作 | | `AtomicBooleanArray` | 原子更新布尔类型数组的元素 | 使用原子的方式更新数组里的某个元素。 ### 二、常用方法 其中`AtomicIntegerArray`和`AtomicLongArray`的使用方式差别不大,`AtomicReferenceArray`因为他的参数为引用数组,所以跟前两个的使用方式有所不同。`AtomicBooleanArray`在生产中使用的很少。 本次只对`AtomicLongArray`和`AtomicReferenceArray`方法进行详细的介绍。 #### 2.1 AtomicLongArray介绍 ##### 构造方法 | 方法名 | 说明 | | ----------------------------- | ------------------------------------------------------------ | | AtomicLongArray(int length) | 创建给定长度的新 `AtomicLongArray` | | AtomicLongArray(long[] array) | 创建与给定数组具有相同长度的新 `AtomicLongArray`,并从给定数组复制其所有元素 | **源码** ```java // 实例化一个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)` | 应用将给定函数利用以后值和给定值的原子更新以后值,返回新值 | ##### 方法示例 ```java import java.util.concurrent.atomic.AtomicLongArray; import java.util.function.LongBinaryOperator; import java.util.function.LongUnaryOperator; /** * @author lilinchao * @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)); } } ``` **运行结果** ``` 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 | **源码** ```java 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` 的元素设置为给定的更新值 | ##### 方法示例 ```java import java.util.concurrent.atomic.AtomicReferenceArray; /** * @author lilinchao * @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()); } } ``` **运行结果** ``` 4 null [12, null, Leefs, [I@63947c6b] ``` **说明**: + 当使用`AtomicReferenceArray(E[] array)`这个构造方法传入一个数组对象时,该数组对象必须是引用类型,int[]不可以,但是Integer[]的可以。 + 当使用`AtomicReferenceArray(int length)`这个构造函数的时候,只要为他指定了数组大小之后,为数组的每一位设置什么值是没有要求的,类似于Map的形式。 ### 三、原子性测试 > 创建10个线程,每个线程分别对数组操作(自增)10000次(采用了函数式编程) ```java /** 参数1:提供数组、可以是线程不安全数组或线程安全数组 参数2:获取数组长度的方法 参数3:自增方法,回传 array, index 参数4:打印数组的方法 */ // supplier 提供者 无中生有 ()->结果 // function 函数 一个参数一个结果 (参数)->结果, BiFunction (参数1,参数2)->结果 // consumer 消费者 一个参数没结果 (参数)->void, BiConsumer (参数1,参数2)->void private static
void demo( Supplier
arraySupplier, Function
lengthFun, BiConsumer
putConsumer, Consumer
printConsumer){ List
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); } ``` **不安全的数组** ```java demo( ()->new int[10], (array)->array.length, (array, index) -> array[index]++, array-> System.out.println(Arrays.toString(array)) ); ``` **运行结果** ``` [6794, 6687, 6664, 6587, 6576, 6491, 6480, 6530, 6536, 6740] ``` **安全的数组** ```java demo( ()->new AtomicIntegerArray(10), (arr)-> arr.length(), (arr,index) -> arr.getAndIncrement(index), (arr)->System.out.println(arr) ); ``` **运行结果** ``` [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000] ```
标签:
并发编程
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2553.html
上一篇
34.并发编程之原子引用
下一篇
36.并发编程之字段更新器
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
数学
数据结构
Java编程思想
递归
Kafka
Flink
数据结构和算法
CentOS
队列
SpringBoot
锁
Quartz
排序
MyBatis-Plus
字符串
并发线程
JavaScript
Spark RDD
Filter
Spark Streaming
国产数据库改造
ajax
机器学习
Java工具类
Golang基础
LeetCode刷题
JavaSE
gorm
Hbase
RSA加解密
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭