李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
35.并发编程之原子数组
Leefs
2022-11-07 PM
558℃
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
标签云
Thymeleaf
数学
持有对象
Quartz
VUE
JavaScript
MyBatisX
DataX
Hadoop
GET和POST
Java
Elasticsearch
Netty
序列化和反序列化
Sentinel
Spark SQL
并发线程
Nacos
字符串
随笔
BurpSuite
ajax
FileBeat
稀疏数组
JavaWEB项目搭建
Hbase
二叉树
nginx
SpringCloudAlibaba
gorm
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞