线程安全性- 原子性
前言目录概述
Atomic 包相关的技术和原理相当的重要, 一定要好好学习,是线程安全三大特性之一
- 原子性
- 有序性
- 可见性
AtomicXXX: CAS、 Unsafe.compareAndSwapXXX原理
一定要掌握CAS的原理,compareAndSwapXXX原理
AtomicLong 和 LongAdder的使用,对比,优缺点,原理。
AtomicReference 和 AtomicReferenceFieldUpdater的使用,对比,优缺点,原理
AtomicStampReference: 解决CAS的 ABA 问题
AtomicLongArray,
AtomicBoolean
原子性-Atomic 包
AtomicXXX: CAS、 Unsafe.compareAndSwapXXX
AtomicLong、 LongAddr
两步操作实现安全
// private static int count = 0;
private static AtomicInteger count= new AtomicInteger(0);
// count++;
count.incrementAndGet();
incrementAndGet 方法的源码(面试)
incrementAndGet – getAndAddInt
可以看到incrementAndGet 使用了unsafe 的类
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
再点开getAndAddInt进去看Unsafe类的实现源码
里面最重要的核心方法 compareAndSwap-CAS,
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;
}
getAndAddInt(Object var1, long var2, int var4)方法详解
- var1: 是传的对象,本例是count对象,count.getAndAddInt()
- var2: 是当前的值, 2+1=3,则var2 = 2
- var4: 2+1=3,则var4 = 1
- var5: 是底层当前的值,如果没有其他线程处理 – 返回 2,不加
点开compareAndSwapInt(var1, var2, var5, var5 + var4));核心
其方法的原理是:
工作内存和主内存的关系,做同步操作才一样
-
count : 工作内存
-
var5 : 底层主内存
compareAndSwapInt(var1, var2, var5, var5: var5 + var4)
拿当前对象里面的值和底层的值作对比,若一样,则执行加操作:var5: var5 + var4 并覆盖,
否则一直循环,直到相同才执行操作。public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
compareAndSwapInt(var1, var2, var5, var5: var5 + var4) -
var1: 是传的对象,本例是count对象,count.getAndAddInt()
-
var2: 是当前的值, 2+1=3,则var2 = 2
-
var4: 2+1=3,则var4 = 1
-
var5: 是底层当前的值,如果没有其他线程处理 – 返回 2,不加
可以看到他是不断的死循环
AtomicLong 和 LongAdder 的比较
compareAndSet
还与一个方法,通过设置boolean,保证一个时间只有一个县城访问
AtomicReference<xxx数据类型>
AtomicReference和AtomicLong的作用相似,核心是:
AtomicXXXFieldUpdater / AtomicReferenceFieldUpdater
AtomicXXXFieldUpdater核心是:修改某一个类的某一个变量或者字段名称,
但是这个变量必须是以volatile的 ,是以非static关键字修饰的才可以(用的不是特别多)
AtomicStampReference: 解决CAS的 ABA 问题
相比其他方法,多了stamp版本,是有每个线程去负责维护的。
直接看核心代码
所以他的解决aba问题的思路是:每次变量更新的时候把变量的版本号加一,每次变量改变,版本号递增。
A:1版本
B:2版本
A:3版本
/*** Atomically sets the value of both the reference and stamp
-
to the given update values if the
-
current reference is {@code ==} to the expected reference
-
and the current stamp is equal to the expected stamp.
-
@param expectedReference the expected value of the reference
-
@param newReference the new value for the reference
-
@param expectedStamp the expected value of the stamp
-
@param newStamp the new value for the stamp
-
@return {@code true} if successful
*/public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
AtomicLongArray,
相比AtomicLong,AtomicInteger额外多一个索引值去更新
相比AtomicLong,它维护一个数组,可以选择性的更改索引的某个值,
public final boolean compareAndSet(int i, long expect, long update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
AtomicBoolean
-
Atomically sets the value to the given updated value
-
if the current value {@code ==} the expected value.
-
@param expect the expected value
-
@param update the new value
-
@return {@code true} if successful. False return indicates that
-
the actual value was not equal to the expected value.
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
实现方法二 - 锁
除了atomic包, 还有sychronized 和 lock,也可以实现原子性
sychronized 依赖于JVM
sychronized 的使用
- 修饰代码块:作用于调用这个东东的对象
- 修饰方法: 作用于调用这个东东的对象
- 修饰静态方法:作用于这个类的所有对象
- 修饰类:作用于这个类的所有对象
Lock : 依赖于特殊的CPU指令,代码实现有ReentrantLock
注意
- 如果父类有的方法有synchronized修饰的, 当一个类继承父类的时候,子类的方法里不带有synchronized这个关键字。
因为synchronized 不属于方法声明
总结:原子性-对比
- synchronized:不可中断锁,适合竞争不激烈,可读性好
- Lock:可中断锁,unlock解锁,多样化同步,竞争激烈时候能维持常态
- Atomic:竞争激烈时候能维持常态,性能比Lock还好