- 概述
- 基本类型(boolean, int, long, 对象)
- AtomicBoolean
- AtomicInteger
- AtomicLong
- AtomicReference
- 数组(Long, Integer, 对象)
- AtomicLongArray
- AtomicIntegerArray
- AtomicReferenceArray
- 字段更新器(指定对象的指定字段)
- AtomicLongFieldUpdater
- AtomicIntegerFieldUpdater
- AtomicReferenceFieldUpdater
- 带版本号引用类型(对象,解决ABA)
- AtomicStampedReference
- AtomicMarkableReference
6.1 概述
- 作用:这些类提供了 基于CAS的原子更新操作,适用于多线程之间共享变量
- 包路径:java.util.concurrent.atomic
- 类图
6.2 基本类型:AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference
(1)底层原理:volatile变量记录数字,调用底层Unsafe类对内存offset进行CAS操作
// AtomicInteger 和 AtomicBoolean
private volatile int value;
// AtomicLong
private volatile long value;
// AtomicReference
private volatile V value;
// AtomicInteger为例,判断当前值与expect是否一致,一致则update
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
(2)API(以AtomicInteger为例)
构造器 |
|
注:无参构造默认为0 |
原子++和-- |
|
|
原子设置值 |
|
通常与while一起使用 |
原子加减 |
|
|
获取 |
|
|
lambda函数 |
|
|
6.3 数组类型:AtomicLongArray, AtomicIntegerArray, AtomicReferenceArray
(1)底层原理:volatile数组变量,需要注意的是,对于AtomicReferenceArray,保护的是数组中的值(用==比较两个Object),而并非数组中的引用(见下面代码注释)
//AtomicLongArray
private final long[] array;
//AtomicIntegerArray
private final int[] array;
//AtomicReferenceArray
private final Object[] array; // must have exact type Object[]
---------------------------------------------------------------------------
//AtomicReferenceArray
//Atomically sets the element at position i to the given updated value if the current value == the expected value.
//true if successful. False return indicates that the actual value was not equal to the expected value
public final boolean compareAndSet(int i, E expect, E update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
(2)API:略,与前者差不多
(3)使用例子
public class Test {
public static void main(String[] args) {
AtomicLongArray array = new AtomicLongArray(10);
array.set(5,10); //对index=5设置值为10
System.out.println("当前值: " + array.get(5)); //10
Boolean bool = array.compareAndSet(5,10,30); //如果array[5]==10,原子更新为30
System.out.println("结果值: " + array.get(5) + " Result: " + bool); //30
}
}
6.4 字段更新器
(1)用法(通过反射使用)
public class Test{
//原子操作类
private static AtomicLongFieldUpdater<Test> updater=AtomicLongFieldUpdater.newUpdater(Test.class,"count");
//原子变量,使用volatile修饰,不能使用static修饰
@Getter
private volatile long count=100;
public static void main(String[] args) {
Test test=new Test();
//如果Test中的count等于100,则更新count为120,返回true
if (updater.compareAndSet(Test,100,120)){
//输出更新后的count
log.info("update success,count: {}",Test.getCount());
}else {
//更新失败,输出count
log.info("update failed,count: {}",Test.getCount());
}
//此时count等于120,所以会返回false
if (updater.compareAndSet(Test,100,120)){
//输出更新后的count
log.info("update success,count: {}",Test.getCount());
}else {
//更新失败,输出count
log.info("update failed,count: {}",Test.getCount());
}
}
}
6.5 带版本号引用类型
(1)ABA问题
- 线程1准备用CAS将变量的值由A替换为C,在此之前,线程2将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题
- AtomicReference会存在ABA问题,因此JUC提供了两个解决方法
(2)AtomicStampedReference:根据版本号判断是否存在ABA问题
- 底层原理:把 Reference, Stamp打包成了静态内部类Pair<Reference, Integer>
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
- 使用
//赋初值“A”,并设置版本号为0
static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A",0);
public static void main(String args[]){
String pre = ref.getReference();
int stamp = ref.getStamp;
//下面这行代码不仅判断pre=="A",还会判断 stamp与最新stamp是否相同,相同则更新版本号和“C”
ref.compareAndSet(pre, "C", stamp, stamp +1)
}
(3)AtomicMarkableReference:有时候,并不关心变量被改了几次,只是关心变量是否被改过
- 使用
AtmoicMarkableReference<String> ref = new AtomicMarkableReference<>("A", true);
String pre = ref.getReference();
//判断当前标记是否为true且当前值是否还是pre,如果都满足表示没有被改过,则更变为"C"和false
ref.compareAndSet(pre, "C", true, false);