当前位置:网站首页>AQS之Atomic详解
AQS之Atomic详解
2022-06-29 09:28:00 【玄郭郭】
目录
什么是原子性
原子性(Atomicity): 指事务的不可分割性,一个事务的所有操作要么不间断地全部被执行,要么一个也没有执行。
Atomic基本属性
在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。
基本类:AtomicInteger、AtomicLong、AtomicBoolean;
引用类型:AtomicReference、AtomicStampedRerence、AtomicMarkableReference;
数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
属性原子修改器(Updater):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
1、原子更新基本类型类
用于通过原子的方式更新基本类型,Atomic包提供了以下三个类:
AtomicBoolean:原子更新布尔类型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新长整型。
AtomicInteger的常用方法如下:
int addAndGet(int delta) :以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果
boolean compareAndSet(int expect, int update) :如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。
int getAndIncrement():以原子方式将当前值加1,注意:这里返回的是自增前的值。
void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。
2、原子更新数组类
通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:
AtomicIntegerArray:原子更新整型数组里的元素。
AtomicLongArray:原子更新长整型数组里的元素。
AtomicReferenceArray:原子更新引用类型数组里的元素。
AtomicIntegerArray类主要是提供原子的方式更新数组里的整型,其常用方法如下
int addAndGet(int i, int delta):以原子方式将输入值与数组中索引i的元素相加。
boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。
3、原子更新引用类型
原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类:
AtomicReference:原子更新引用类型。
AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子的更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(VinitialRef, boolean initialMark)
4、原子更新字段类
如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
AtomicLongFieldUpdater:原子更新长整型字段的更新器。
AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值
与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。
原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater创建一个更新器。原子更新类的字段的必须使用public volatile修饰符。
什么是CAS
先聊聊什么是CAS,compareAndSwap ,对比并且替换,顾名思义,先对比旧参数,相等则替换成新参数。
说到CAS,就要讲到Unsafe这个类。这个类就有点牛逼了,因为他可以操作底层的一些资源,比如系统内存资源等
Unsafe提供的API大致可分为内存操作、CAS、Class相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作等几类,我就不一一介绍了。我们主要了解一下其中的CAS。
我们进入Unsafe类中,可以看到这三个本地方法:
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
以compareAndSwapInt来分析一下他的参数的含义:var1就是要修改这个参数的对象,var2对象中参数的偏移量,因为对象和参数是存中cache line中,这个偏移量就是找到这个参数是在cache line的哪个具体的位置,通过对象的基地址+偏移量 = 参数的内存地址,就理解成通过这个var2找到参数的位置。var4就是更新前这个值是什么。var5更新后的值。
例如:有一个变量a=1,有两个线程同时来操作这个变量,
t1. compareAndSwapInt(this,valueOffset,1,2);
t1. compareAndSwapInt(this,valueOffset,6,8);
那么t1会成功,t2则会失败。因为CAS会先先将第三个参数与变量a相比较,如果相等的话,则更新变量a的值。
AtomicInteger源码解析
拿AtomicInteger 来举例
static AtomicInteger atomicInteger = new AtomicInteger(1);
public static void main(String[] args) {
for(int i=0 ;i<10;i++){
new Thread(()->{
boolean isState = atomicInteger.compareAndSet(1,5);
if(isState){
System.out.println("CAS修改成功;"+Thread.currentThread().getName());
}else{
System.out.println("CAS修改失败");
}
}).start();
}
}假设这10个线程是同时运行的,那么只有一个线程能够CAS修改成功。

compareAndSet分析:
//若原先的值与expect相等,则替换成update,返回true;否则失败,返回false
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}再来看一个例子:
假设这10个线程是同时运行的,那么最终的atomicInteger值也会是我们想要的20,而不会小于20.因为getAndAddInt方法中有一个while循环,是保证多个线程进来每个线程都能够执行成功,CAS算法是保证不管多少个线程通常更改变量,有且仅有一个能够更改成功,其余的未修改成功的会再次循环,再次尝试CAS来更新变量,直至成功为止。
static AtomicInteger atomicInteger = new AtomicInteger(10);
public static void main(String[] args) {
for(int i=0 ;i<10;i++) {
new Thread(()->{
//参数加1
int result = atomicInteger.addAndGet(1);
System.out.println(Thread.currentThread().getName()+"==>"+result);
}).start();
}
}//源码部分
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
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;
}
Unsafe的线程调度
Unsafe类对线程的调度中在AQS中常用到的阻塞和唤醒,LockSupport.park()和LockSupport.unpark()其实底层调的就是Unsafe的park和unpark
//线程的唤醒 public native void unpark(Object var1); //线程的阻塞 public native void park(boolean var1, long var2);
边栏推荐
- Slide the custom control to close the activity control
- 《CLR via C#》读书笔记-加载与AppDomain
- 打印9*9乘法口诀表(C语言)
- We can't tell the difference between distributed and cluster. Let's tell the story of two cooks cooking
- Rikka with cake (segment tree + segment tree)
- Contents of advanced mathematics
- 2020-10-17:刷题1
- Is it safe to open a stock account with the QR code given by the manager of a securities firm? I want to open an account
- 2019.10.20 training summary
- Virtual machine port scanning
猜你喜欢
随机推荐
C#使用WinExec调用exe程序
Analysis of liferayportal jsonws deserialization vulnerability (cve-2020-7961)
L2-031 深入虎穴 (25 分)
MySQL innodb每行数据长度的限制
Beautiful ruins around Kiev -- a safe guide to Chernobyl!
L2-3 这是二叉搜索树吗?-题解超精彩哦
1021 Deepest Root (25 分)
Judgment of points inside and outside polygon
L1-009 N个数求和 (20 分)
Text of the basic component of the shutter
[51nod 1215] array width
Win32exception (0x80004005): This program is blocked by group policy. For more information, contact your system administrator.
mysql 8.0 一条insert语句的具体执行流程分析(三)
HDU 6778 car (group enumeration -- > shape pressure DP)
Download control 1 of custom control (downloadview1)
manacher
How to quickly complete disk partitioning
BUUCTF--内涵的软件
这个开源项目超哇塞,手写照片在线生成
Dynamic planning summary









