6.juc包下的原子类AtomicInteger,AtomicLong等AtomicXXX介绍
在介绍juc中的原子类之前,先看看官方文档对java.util.concurrent.atomic
包的介绍官方文档地址这里截取翻译之后的部分描述
1. 支持对单个变量进行无锁线程安全编程
2. 类的实例`AtomicBoolean`,`AtomicInteger`,`AtomicLong`和`AtomicReference` 每个提供访问和更新相应的类型的单个变量
3. 这些类不是 java.lang.Integer和相关类的通用替代品。他们没有 定义方法,如equals,hashCode和 compareTo。由于预期原子变量会发生突变,因此对于哈希表键,它们是较差的选择。
4. `AtomicIntegerArray`,`AtomicLongArray`和`AtomicReferenceArray`类进一步扩展到这些类型的数组原子操作的支持
5. `AtomicIntegerFieldUpdater`,`AtomicLongFieldUpdater`和`AtomicReferenceFieldUpdater`是基于反射的实用程序,它们提供对关联的字段类型的访问。这些主要用于原子数据结构,其中几个volatile同一节点的字段(例如,树节点的链接)独立地接受原子更新。这些类在如何以及何时使用原子更新方面提供了更大的灵活性,但代价是基于反射的设置更加笨拙,使用不方便且保证较弱。
6. `AtomicMarkableReference`类与引用关联的单个布尔值。例如,此位可能在数据结构内使用,表示所引用的对象在逻辑上已被删除。`AtomicStampedReference`类与引用关联的整数值。例如,这可以用于表示与一系列更新相对应的版本号。
在整个包中可以分为5种,这5中是我个人按照作用进行分类的
种类 | 包含的类 |
---|---|
普通原子操作类 |
AtomicBoolean ,AtomicInteger ,AtomicLong 和AtomicReference
|
数组操作类 |
AtomicIntegerArray ,AtomicLongArray 和AtomicReferenceArray
|
对象中字段操作类 |
AtomicIntegerFieldUpdater ,AtomicLongFieldUpdater 和AtomicReferenceFieldUpdater
|
标记操作类 |
AtomicMarkableReference 跟AtomicStampedReference
|
并发辅助计算类 |
DoubleAccumulator ,DoubleAdder ,LongAccumulator ,LongAdder 以及这些类的抽象父类Striped64
|
1.作用
从上面介绍中可以看出这个类的作用是用来对单个变量进行操作的,在并发环境下可以保证线程的安全性,其中保证安全的原因是使用CAS操作对变量进行操作的,对于这种CAS操作的实现可以查看前面的一篇文章CAS以及相关的底层实现。
2.普通原子操作类
个人划分的普通原子操作类是AtomicBoolean
,AtomicInteger
,AtomicLong
和AtomicReference
各个类的作用分别是
类名 | 作用 |
---|---|
AtomicBoolean
|
一个可以原子更新值boolean值的类 |
AtomicInteger
|
一个可以原子更新int类型变量值的类 |
AtomicLong
|
一个可以原子更新long类型变量值的类 |
AtomicReference
|
一个可以原子更新对象引用的类 |
2.1 AtomicInteger
,AtomicLong
类
AtomicLong
中的方法跟 AtomicInteger
中提供的方法是一模一样的,这里用代码简单演示一下AtomicLong
中的方法
public class AtomicTest {public static void main(String[] args) {AtomicInteger integer = new AtomicInteger();//设置值integer.set(1);//跟set一样也是设置值,只不过set方法能保证可见性,而lazySet不行integer.lazySet(2);//CAS操作,比较交换integer.compareAndSet(2,3);//自增后获取值integer.incrementAndGet();//与指定的值相加后返回integer.addAndGet(2);//获取之后进行自增integer.getAndIncrement();System.out.println(integer.get());}
}
这里只列举了部分的方法,大部分方法的作用看方法名就能明白。关于set
方法跟lazySet
的区别,这里可以看前面写的一篇文章java的JUC包下AtomicXXX中的set跟lazySet区别以及lazySet的原理。
2.2 AtomicBoolean
类
AtomicBoolean
其实是用一个int
类型的值来表示true
跟false
的,如果是true
则用1表示,如果是false
则用0表示。其初始化方法就可以看出来
public AtomicBoolean(boolean initialValue) {value = initialValue ? 1 : 0;}
因此其底层的实现的方式跟AtomicBoolean
是一样的,还是简单的列举一下部分方法
public class AtomicBooleanTest {public static void main(String[] args) {AtomicBoolean atomicBoolean = new AtomicBoolean(true);System.out.println(atomicBoolean.get());boolean setResult1 = atomicBoolean.compareAndSet(false, true);System.out.println(setResult1);boolean setResult2 = atomicBoolean.compareAndSet(true, false);System.out.println(setResult2);System.out.println(atomicBoolean.get());}
}
运行结果
true
false
true
false
2.3 AtomicReference
类
AtomicReference
作用是更新一个对象。维护的是这个变量的地址值。在更新的时候校验这个的对象是不是初始化时候的对象的引用地址,不是则不给予更新,是的就更新。同时还可以定义更新的操作函数。这里列举部分测试代码
public class AtomicReferenceTest {@Testpublic void test() {Object referenceOne = new Object();Object referenceTwo = new Object();AtomicReference<Object> atomicReferenceOne = new AtomicReference<>(referenceOne);System.out.println(atomicReferenceOne.get());//compareAndSet时候原始的对象必须是创建AtomicReference的时候设置的对象boolean resultOne = atomicReferenceOne.compareAndSet(referenceTwo, referenceOne);System.out.println(resultOne);//compareAndSet时候原始的对象必须是创建AtomicReference的时候设置的对象boolean resultTwo = atomicReferenceOne.compareAndSet(referenceOne, referenceTwo);System.out.println(resultTwo);//获取然后更新函数执行结果返回的值atomicReferenceOne.getAndUpdate((one -> referenceTwo));System.out.println(atomicReferenceOne.get());}
}
3.数组操作类
用来操作数组的原子类AtomicIntegerArray
,AtomicLongArray
和AtomicReferenceArray
。分别对应操作int数组,long数组以及object
类型数组。
3.1AtomicIntegerArray
,AtomicLongArray
,AtomicReferenceArray
类
这里用AtomicIntegerArray
作为示例,因为这两个类实现是一样的,只不过接收的数据类型不一有。先看看对应的重要部分,偏移量的计算方式
static {//获取当前数组的规模int scale = unsafe.arrayIndexScale(long[].class);//数组的大小需要是2的倍数if ((scale & (scale - 1)) != 0)throw new Error("data type scale not a power of two");//计算当前数组的长度是2的多少倍shift = 31 - Integer.numberOfLeadingZeros(scale);}private static long byteOffset(int i) {//计算当前元素的实际偏移量=数组的起始偏移量+当前元素在数组的位置return ((long) i << shift) + base;}
知道了计算偏移量的方式,就好操作数组中的对应的元素了。所有的对数组中数据的操作,都是通过偏移量来进行的。
public class AtomicIntegerArrayTest {@Testpublic void test() {AtomicIntegerArray array = new AtomicIntegerArray(new int[]{1, 2, 3, 4,5});//获取指定index=2的数据System.out.println(array.get(2));//在指定index=2的数据上加上1后获取结果System.out.println(array.addAndGet(2,1));//获取指定index=2的数据System.out.println(array.get(2));//比较交换指定index=2的值为4,因为值是4而期望的3所以失败System.out.println(array.compareAndSet(2,3,4));;//比较交换指定index=2的值为3,因为值是4期望的4所以成功System.out.println(array.compareAndSet(2,4,3));;}
}
4.对象字段操作类
4.1AtomicIntegerFieldUpdater
,AtomicLongFieldUpdater
和AtomicReferenceFieldUpdater
AtomicIntegerFieldUpdater
,AtomicLongFieldUpdater
和AtomicReferenceFieldUpdater
都是对一个指定对象中的指定字段进行操作。这里需要注意以下几点
- 对应的字段必须非
private
修饰 - 对应的字段必须是
volatile
修饰的 AtomicIntegerFieldUpdater
操作的字段必须是int
类型,AtomicLongFieldUpdater
操作的字段必须是long
类型,AtomicReferenceFieldUpdater
的操作字段必须是创建AtomicReferenceFieldUpdater
对象时候指定字段的类型
这里解释一下为什么要满足上面几点。
- 因为这些类对字段进行操作都是利用反射进行操作的。
- 要保证原子性以及可见性就需要用
volatile
修饰 AtomicIntegerFieldUpdater
会判断操作字段是不是int
类型,同理AtomicLongFieldUpdater
会判断是不是long
类型
列举一下部分方法以及使用方式
public class AtomicXXXFieldUpdaterTest {@Testpublic void test() {TestObject objectOne = new TestObject();//创建一个指定对象指定字段的操作的AtomicIntegerFieldUpdaterAtomicIntegerFieldUpdater<TestObject> integerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(TestObject.class, "age");//compareAndSet指定对象的某个int类型字段System.out.println(integerFieldUpdater.compareAndSet(objectOne,23,22));System.out.println(integerFieldUpdater.compareAndSet(objectOne,22,23));//创建一个指定对象指定字段的操作的AtomicLongFieldUpdaterAtomicLongFieldUpdater<TestObject> longFieldUpdater = AtomicLongFieldUpdater.newUpdater(TestObject.class, "years");//compareAndSet指定对象的某个long类型字段System.out.println(longFieldUpdater.compareAndSet(objectOne,2019L,2018L));System.out.println(longFieldUpdater.compareAndSet(objectOne,2018L,2019L));//创建一个指定对象指定类型字段的操作的AtomicReferenceFieldUpdaterAtomicReferenceFieldUpdater<TestObject,String> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(TestObject.class,String.class, "name");//compareAndSet指定对象的指定类型字段System.out.println(referenceFieldUpdater.compareAndSet(objectOne,"szh","acy"));System.out.println(referenceFieldUpdater.compareAndSet(objectOne,"acy","szh"));}
}class TestObject{protected volatile String name="acy";public volatile int age=22;protected volatile long years=2018L;public Long getYears() {return years;}public void setYears(Long years) {this.years = years;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}
5. 标记操作类
5.1 AtomicMarkableReference
跟AtomicStampedReference
AtomicMarkableReference
跟AtomicStampedReference
都是标记一个类,然后根据类的引用以及标记来判断是否允许操作的类。其中AtomicMarkableReference
只能标记为true
或者false
。AtomicStampedReference
的标记只能是int
类型的,通常作为更新的版本号,也就是用来解决ABA问题。两者的实现方式相同,都是维护一个内部类分别记录对象以及版本号。简单介绍一下用法。
public class AtomicXXXFieldUpdaterTest {@Testpublic void test() {Object objOne = new Object();Object objTwo = new Object();AtomicMarkableReference<Object> markableReference = new AtomicMarkableReference<>(objOne, true);//设置标记需要reference符合System.out.println(markableReference.attemptMark(objTwo,false));System.out.println("设置mark需要reference符合"+markableReference.attemptMark(objOne,false));//设置成功需要reference跟标记都是符合的System.out.println(markableReference.compareAndSet(objTwo,objOne,false,true));System.out.println(markableReference.compareAndSet(objTwo,objOne,true,false));System.out.println("设置成功需要reference跟mark都是符合的-----"+markableReference.compareAndSet(objOne,objTwo,false,true));AtomicStampedReference<Object> stampedReference = new AtomicStampedReference<>(objOne, 0);//设置标记需要reference符合System.out.println(stampedReference.attemptStamp(objTwo,2));System.out.println("设置stamp需要reference符合"+stampedReference.attemptStamp(objOne,1));//设置成功需要reference跟标记都是符合的System.out.println(stampedReference.compareAndSet(objTwo,objOne,0,1));System.out.println(stampedReference.compareAndSet(objTwo,objOne,1,0));System.out.println("设置成功需要reference跟stamp都是符合的------"+stampedReference.compareAndSet(objOne,objTwo,1,0));}
}
6.并发辅助计算类
6.1Striped64
,DoubleAccumulator
,DoubleAdder
,LongAccumulator
,LongAdder
Striped64
是一个可以在并发环境下面计数用的一个组件。这个类的思想可以参考ConcurrentHashMap
的size
方法。Striped64的设计思路是在竞争激烈的时候尽量分散竞争,在实现上,Striped64维护了一个base Count和一个Cell数组,计数线程会首先试图更新base变量,如果成功则退出计数,否则会认为当前竞争是很激烈的,那么就会通过Cell数组来分散计数,Striped64根据线程来计算哈希,然后将不同的线程分散到不同的Cell数组的index上,然后这个线程的计数内容就会保存在该Cell的位置上面,基于这种设计,最后的总计数需要结合base以及散落在Cell数组中的计数内容。这里可以参考一下这篇博文并发之Striped64。
DoubleAccumulator
,DoubleAdder
,LongAccumulator
,LongAdder
都是Striped64
的字类。主要是对数相加跟求和以及转化上的方法。这里就不多介绍了。
6.juc包下的原子类AtomicInteger,AtomicLong等AtomicXXX介绍相关推荐
- 源码学习【原子类AtomicInteger】Java原子类底层实现(解释详细)
原子类AtomicInteger 在Java中,有很多方法可以保证多线程下数据的安全,AtomicXXXX这些类就是其中的一种,原子类,可以保证每一步操作都是原子操作.这次就对AtomicIntege ...
- java原子类场景,CAS你知道吗?原子类AtomicInteger的ABA问题谈谈?,原子共面问题...
CAS你知道吗?原子类AtomicInteger的ABA问题谈谈?,原子共面问题(1)CAS是什么? 比较并交换 举例1, CAS产生场景代码? importjava.util.concurrent ...
- 多线程十 JUC包下的常用工具类
JUC包下的常用工具类 1. CountDownLatch-闭锁 2. CyclicBarrier-循环栅栏 3. Semaphore-信号量 4. Exchanger-线程数据交换器 这篇文章主要是 ...
- java并发:原子类之AtomicLong
原子类之AtomicLong java线程中的操作,需要满足原子性.可见性等原则,比如i++这样的操作不具备原子性, A线程读取了i,另一个线程执行i++,A线程再执行i++就会引发线程安全问题 推荐 ...
- java atomiclong 使用_java并发:原子类之AtomicLong
原子类之AtomicLong java线程中的操作,需要满足原子性.可见性等原则,比如i++这样的操作不具备原子性, A线程读取了i,另一个线程执行i++,A线程再执行i++就会引发线程安全问题 推荐 ...
- JUC多线程:Atomic原子类与CAS原理
一.Atomic 原子类的原理: Atomic 原子操作类是基于无锁 CAS + volatile 实现的,并且类中的所有方法都使用 final 修饰,进一步保证线程安全.而 CAS 算法的具体实现方 ...
- 多线程与高并发(三):JUC包下新的同步机制:CAS,AtomicInteger,AtomicLong,ReentrantLock,CountDownLatch,ReadWriteLock等
CAS CAS 是一种乐观锁,syncronized 是一种悲观锁 AtomicInteger AtomicInteger count = new AtomicInteger(0); /*synchr ...
- java原子整数_多线程(四、原子类-AtomicInteger)
案例 10个线程并发累加一个整数,每个线程累加1000,保证线程安全 Unsafe类,来源于sun.misc包.该类封装了许多类似指针操作,可以直接进行内存管理.操纵对象.阻塞/唤醒线程等操作. pa ...
- 111 多线程JUC包下代码分析
2019独角兽企业重金招聘Python工程师标准>>> Java多线程系列目录(共43篇) AtomicLongFieldUpdater:通过反射+CAS实现对传入对象的指定long ...
最新文章
- php数据库html文本,关于php,mysql,html的数字分页和文本_php
- Windows 7 扩展玻璃效果(Aero Glass)
- Go 导入当前项目下的包
- 解决Winform程序在不同分辨率系统下界面混乱问题
- 数据科学家数据分析师_站出来! 分析人员,数据科学家和其他所有人的领导和沟通技巧...
- g开头的C语言编程软件,C语言函数大全(g开头)
- Python最实用的25个小技巧
- 开源内容管理系统 php mysql_十大免费PHP+MySql平台内容管理系统推荐
- 收藏 | 让你纵横 GitHub 的五大神器
- Linux教程系列-命令大全
- 毫米波雷达测距/测速原理介绍_小七自学笔记
- 使用JavaScript使浏览器进入全屏或退出全屏
- ​2 万字系统总结,带你实现 Linux 命令自由!
- Transphorm第三代经JEDEC认证的GaN半导体将助力稳态光电全新的1.6 kW钛金级ATX PC游戏电源
- 陈平原:《阅读大学的六种方式》[转]
- 一种基于深度学习的目标检测提取视频图像关键帧的方法
- Android开发越来越式微了吗?,flutter二维码扫描第三方
- 单点登录(一)| LDAP 协议
- win11安装Ubuntu错误Installing, this may take a few minutes… WslRegisterDistribution failed with error: 0
- 支付宝产品签约-“系统综合评估签约条件不满足”解决办法