AtomicInteger
AtomicInteger可以保证在多线程环境下正确地增减
底层使用的是cas,volatile,unsafe来实现的
重要属性
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
// 通过unsafe获取指定属性的偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// 将value声明为voliatile可以保证可见性,但是不能保证原子性
// 即一个线程成功修改了volatile修饰的变量的值,会令其他线程的工作内存中该变量的副本失效,使得其他线程再去读取这个变量值时是从内存中读取最新的值
// 如果不使用volatile修饰,那么一个线程修改了变量的值,只修改了工作内存中的值,并没有修改主内存中的值,因此其他变量读取时仍然读取的是工作内存中的旧的值
private volatile int value;
构造函数
构造函数十分简单,就是简单的赋值
public AtomicInteger(int initialValue) {
value = initialValue;
}
修改操作
public final int addAndGet(int delta) {
// this代表当前AtomicInteger的地址,valuesOffset代表value属性的偏移量
// 使用cas进行更新
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;
}
AtomicStampedReference
AtomicStampedReference主要使用来解决ABA问题的
所谓ABA问题指的就是在A线程进行cas操作时,首先读取了变量的值为0,然后时间片用完,线程B通过cas成功将变量从0更改为1,然后又通过cas将变量从1更改为0,线程B时间片用完,线程A获得时间片,此时再读取变量的值仍然为0,那么线程A就判断变量没有改变,即没有线程对该变量操作,接着线程A就修改变量的值
解决方法就是另外使用一个版本号,在判断变量是否修改时,不光要判断变量的值是否改变,还需要判断版本号是否改变,只有这两个都没有改变,我们才任务变量没有被其他线程改变
属性
AtomicStampedReference底层最重要的属性就是一个Pair类对象
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);
}
}
private volatile Pair<V> pair;
修改
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
// 当前引用和期望的引用相同,版本号也一致
// 代表没有其他线程修改变量
// 接下来尝试set
// 如果新的引用和现有引用相同并且新的版本号和现有版本号也相同,那么就不需要再set了
// 如果不同,那么就尝试使用cas修改内存中的变量值
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
```java
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}