通过CAS机制来实现对于某一个字段某一个数组等进行原子操作,保证是线程安全的,例如i++的线程安全也可以通过原子操作类来实现,何为CAS我们最后讲
原子更新基本类型类
- AtomicBoolean
- AtomicInteger
- AtomicLong
| 方法 | 解释 |
|---|---|
| int addAndGet(int delta) | 以原子的方式将输入的数值与实例中的值相加,并返回结果 |
| boolean compareAndSet(int expect,int update) | 如果输入的数值等于预期值,则以原子方式将该值设置为输入的值 |
| int getAndIncrement() | 以原子的方式将当前值加1,返回自增之前的值 |
| void lazySet(int newValue) | 最终会设置为newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值 |
| int getAndSet(int newValue) | 以原子方式设置为newValue的值,并返回旧值 |
//AtomicInteger
@Test
public void testAtomicInteger() throws InterruptedException {
AtomicInteger atomicInteger = new AtomicInteger(1);
int iag = atomicInteger.incrementAndGet();
System.out.println("incrementAndGet的结果为:"+iag);
atomicInteger.compareAndSet(2,1);
System.out.println("compareAndSet后的结果是:"+atomicInteger.get());
}
public final int getAndIncrement() {
for (;;) {
int current = get(); // 获取当前值
int next = current + 1; // 计算下一个值
if (compareAndSet(current, next)) // 使用CAS操作尝试更新值
return current; // 返回旧值
}
}
public final boolean compareAndSet(int expect, int update) {
//如果期望值 等于目前值,那么就能进行修改
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
其他两个也是同样的道理,但在Unsafe类中,只提供了3种CAS方法,而对于Boolean类型的操作,是先把Boolean转换成整型,在使用compareAndSwapInt进行CAS
原子更新数组
- AtomicIntegerArray:原子更新整型数组里的元素
- AtomicLongArray:原子更新长整型数组里的元素
- AtomicReferenceArray:源自更新引用类型数组里的元素
| 方法 | 解释 |
|---|---|
| int addAndGet(int i,int delta) | 以原子方式将输入值于数组中索引i的元素相加 |
| boolean compareAndSet(int i ,int expect,int update) | 如果当前值等于预期值,则以原子的方式进行队索引i元素的修改。 |
数组value通过构造方法传递进去,然后AtomicIntegerArray会将当前数组复制一份,所以当AtomicIntegerArray对内部的数组元素进行修改时,不会影响传入的数组。
@Test
public void testAtomicIntegerArray(){
int[] arr = new int[]{1,2,3,4};
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
atomicIntegerArray.compareAndSet(2,3,1);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
}
}
@Test
public void testAtomicIntegerArray(){
int[] arr = new int[]{1,2,3,4};
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
atomicIntegerArray.compareAndSet(2,3,1);
//经过修改后的arr数组
System.out.print("经过修改后的arr数组:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
}
System.out.println();
System.out.print("获取修改后AtomicIntegerArray数组:");
//获取修改后AtomicIntegerArray数组
for (int i = 0; i < arr.length; i++) {
System.out.print(atomicIntegerArray.get(i));
}
}
说明这两个数组并不是同一个
原子更新引用类型
- AtomicReference:原子更新引用类型
- AtomicReferenceFieldUpdate:原子更新引用类型里的字段
- AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和引用类型
原子更新字段类
如果需要原子的更新某个类的某个字段,那么就可以使用原子更新字段类
- AtomicIntegerFieldUpdate:原子更新整型的字段的更新器。
- AtomicLongFieldUpdate:原子更新长整型字段的更新器。
- AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。
CAS
CAS 全称 Compare And Swap(比较与交换),是一种用于在多线程环境下实现同步功能的机制。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。java.util.concurrent 包中的原子类就是通过 CAS 来实现了乐观锁。
CAS的具体实现
- CAS操作包含三个操作数:需要更新的内存值V、进行比较的预期数值A和要写入的值B。
- CAS的实现逻辑,是将需要更新的内存值V和预期值A进行比较,当且仅当V的值等于A的时候,CAS通过原子方式用新值B来更新V的值("比较+更新"整体是一个原子操作),否则不会执行任何操作。
- 一般情况下,"更新"是一个不断重试的操作。
- 我们以java.util.concurrent.atomic 并发包下 AtomicInteger 原子整型类为例,一起来分析下 CAS 底层实现机制。
- atomicData.incrementAndGet();
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//Unsafe类中的方法
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;
}
从上面的代码可以见到AtomicInteger的内部方法都是基于Unsafe类实现的,Unsafe类是一个底层的指令复制工具类,主要看这个compareAndSet()方法的返回:
unsafe.compareAndSwapInt(this, valueOffset, expect, update);
- this就是Unsafe对象本身,需要通过这个类来获取value的内存偏移地址
- valueOffset,value变量的内存偏移地址
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
- expect,期望更新的值
- update,要更新的最新值
如果原子变量中的 value 值等于 expect,则使用 update 值更新该值并返回 true,否则返回 false。
private volatile int value;
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
这里的value实际变量,是由volatile关键字修饰的,为了保证在多线程下内存的可见性