Atomic(原子相关类)# 系列文章目录
为了保证对变量的正确修改我们使用JUC并发包中的原子类
文章目录
并发包(原子类)
提示:以下是本篇文章正文内容,下面案例可供参考
一、基本类型原子类
- AtomicLong:long类型
- AtomicInteger:int类型
- AtomicBoolean:布尔类型
AtomicInteger源码分析
构造器
//无参构造时成员变量value默认是0
public AtomicInteger() {
}
//指定value初始值
public AtomicInteger(int initialValue) {
value = initialValue;
}
成员变量
private volatile int value; //实际存值的变量,volatile修饰内存保证可见性
private static final long valueOffset;// value在对象的偏移量
private static final Unsafe unsafe = Unsafe.getUnsafe();//魔法类,原子操作都依赖其实现
静态初始化块
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value")); //计算value偏移量
} catch (Exception ex) { throw new Error(ex); }
}
主要方法
//获取成员变量的值(读操作是线程安全的)
public final int get() { return value;}
//设置成员变量的值(直接设置值由于不依赖与先前的值所以无需cas)
public final void set(int newValue) { value = newValue;}
//自增操作通过unsafe实现安全自增,返回自增后的结果
public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;}
//自减同上
public final int decrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, -1) - 1;}
//与上类似(返回旧值)
public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}
//与上类似(返回旧值)
public final int getAndDecrement() {return unsafe.getAndAddInt(this, valueOffset, -1);}
//通过unsafe安全的增加delta(返回旧值)
public final int getAndAdd(int delta) return unsafe.getAndAddInt(this, valueOffset, delta);}
//通过unsafe安全的设置newValue(返回旧值)
public final int getAndSet(int newValue) {return unsafe.getAndSetInt(this, valueOffset, newValue);}
//强制转换相关
public float floatValue() {return (float)get();}
public double doubleValue() {return (double)get();}
public long longValue() {return (long)get();}
小结 :由上可以我们通过volatile和Unsafe来保证变量原子性的特征
二、数组类型原子类
- AtomicLongArray:long型数组
- AtomicIntegerArray:int型数组
- AtomicReferenceArray:引用类型数组
AtomicIntegerArray源码分析
构造方法
//指定一个数组长度来构造
public AtomicIntegerArray(int length) {
array = new int[length];
}
//直接传入一个数组来构造
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}
成员变量
private static final Unsafe unsafe = Unsafe.getUnsafe();//魔法类
private static final int base = unsafe.arrayBaseOffset(int[].class);//数组起始为止偏移
private static final int shift;
private final int[] array;//实际存数据的数组
静态初始化块
static {
int scale = unsafe.arrayIndexScale(int[].class); //返回数组中元素大小(此处int为4)
if ((scale & (scale - 1)) != 0) //scale必须为2的次幂
throw new Error("data type scale not a power of two");
// numberOfLeadingZeros计算scale二进制高位0的个数此处为29(遇到第一个不为0 停止)
//shift表示数组元素大小2的次幂的指数这里int为4 = 2^2 所以shift为2
//之后计算数组偏移时直接使用base + (i << shift)
shift = 31 - Integer.numberOfLeadingZeros(scale);
}
主要方法
//保证可见性的方式获取数组中的元素
public final int get(int i) {
return getRaw(checkedByteOffset(i));
}
//数组边界检查
private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return byteOffset(i); //直接返回数组中下标为i的变量偏移
}
//计算数组中元素的偏移
private static long byteOffset(int i) {
return ((long) i << shift) + base; //基址 + 偏移
}
//根据偏移直接从数组中获取元素
private int getRaw(long offset) {
return unsafe.getIntVolatile(array, offset);
}
//保证内存可见的设置下标位置为i的元素
public final void set(int i, int newValue) {
unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
}
//保证内存可见的设置下标位置为i的元素,并返回旧值
public final int getAndSet(int i, int newValue) {
return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
}
//通过CAS改变下标为i位置的元素,成功返回ture
public final boolean compareAndSet(int i, int expect, int update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
//原子的加1
public final int getAndIncrement(int i) {
return getAndAdd(i, 1);
}
//原子的减1
public final int getAndDecrement(int i) {
return getAndAdd(i, -1);
}
//设置新值(其他线程在一小段时间内还是会返回旧值)
public final void lazySet(int i, int newValue) {
unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
}
小结 :AtomicIntegerArray保证了对数组中每个元素的操作都是安全的。
三、引用类型原子类
- AtomicReference:与AtomicInteger类似,该类保证对象的修改的安全性
- AtomicStampedReference
- AtomicMarkableReference
AtomicStampedReference源码分析
CAS存在ABA问题:CAS是通过比较数据是否为期望值,其目的是判断数据是否发生改变,但是当数据从A变到B又变到A时实际上数据发生了改变但是CAS认为其没有发生改变。
构造方法
//传入一个需要保护的对象和一个版本号
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
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);
}
}
可见AtomicStampedReference中的对象实际存在于静态内部类中,还需要传入一个版本号,来记录对象的修改。
成员变量
private volatile Pair<V> pair; //实际存对象的静态内部类
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); //pair内存偏移
主要方法
public V getReference() { //获取对象
return pair.reference;
}
public int getStamp() { //获取当前对象版本号
return pair.stamp;
}
//如果预取对象和版本号 和 当前的相同 就CAS修改pair 为 新的对象和版本号成功返回ture
//如果预取对象和版本号 和 当前的不相 直接返回false
public boolean weakCompareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
return compareAndSet(expectedReference, newReference,
expectedStamp, newStamp);
}
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;//当前pair(对象+版本号)
return
expectedReference == current.reference &&
expectedStamp == current.stamp && //判断期望对象和版本号和当前的是否相同
((newReference == current.reference &&
newStamp == current.stamp) || //判断新对象和版本号是否和当前的相同
casPair(current, Pair.of(newReference, newStamp))); //创建一个新的pair
}
//CAS改变pair变量,成功返回true
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
//设置新的对象和版本号(如果和当前版本号和对象都相不修改)
public void set(V newReference, int newStamp) {
Pair<V> current = pair;
if (newReference != current.reference || newStamp != current.stamp)
this.pair = Pair.of(newReference, newStamp);
}
//尝试修改版本号不修改对象(如果对象已被修改则版本号修改失败)
public boolean attemptStamp(V expectedReference, int newStamp) {
Pair<V> current = pair; //当前pair
return
expectedReference == current.reference && //对象期望值和当前值是否相同
(newStamp == current.stamp || //版本号已经是想修改的值
casPair(current, Pair.of(expectedReference, newStamp)));
}
小结:AtomicStampedReference引入版本号解决了CAS中存在的ABA问题。AtomicMarkableReference与AtomicStampedReference类似,不过版本只有两种状态即ture和false。
四、对象属性修改原子类
- abstract AtomicLongFieldUpdater
- abstract AtomicIntegerFieldUpdater
- abstract AtomicReferenceFieldUpdater
AtomicIntegerFieldUpdater源码分析
构造方法
protected AtomicIntegerFieldUpdater() { //抽象类是无法被直接构造的
}
//AtomicIntegerFieldUpdater通过静态方法创建(创建的实际上是其内部类)
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}
// AtomicIntegerFieldUpdaterImpl构造方法
AtomicIntegerFieldUpdaterImpl(final Class<T> tclass, //需要升级的类
final String fieldName,//需要升级类中变量名
final Class<?> caller) {
……
if (field.getType() != int.class) //升级的变量必须为intl类型
throw new IllegalArgumentException("Must be integer type");
if (!Modifier.isVolatile(modifiers)) //升级的变量必须是volatile类型
throw new IllegalArgumentException("Must be volatile type");
this.cclass = (Modifier.isProtected(modifiers) &&
tclass.isAssignableFrom(caller) &&
!isSamePackage(tclass, caller))
? caller : tclass;
this.tclass = tclass; //需要升级类的class
this.offset = U.objectFieldOffset(field);//变量在该类中的偏移
}
// AtomicIntegerFieldUpdaterImpl成员方法
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();//unsafe
private final long offset; //变量偏移
private final Class<?> cclass;
private final Class<T> tclass; //持有字段的类
其他一些方法(实现了AtomicIntegerFieldUpdater)
小结:AtomicIntegerFieldUpdater是用于类中变量的修改的安全性,对变量的升级,变量必须是volatile并且是int类型的。我们可以通过对类中变量进行升级然后通过该类来安全的操作变量,对变量进行修改。AtomicInteger也可以为什么还要它,AtomicInteger创建本身需要一定的性能,有时候我们可能只需要偶尔的对变量进行原子操作,此时AtomicIntegerFieldUpdater是不错的选择。如果的列子来理解AtomicIntegerFieldUpdater的好处。
例子
public class Test implements Runnable {
private volatile int num; //变量必须是int 和 volatile
private static Test unsafeClass = new Test();
private static Test safeClass = new Test();
//变量升级
AtomicIntegerFieldUpdater<Test> up = AtomicIntegerFieldUpdater.newUpdater(Test.class,"num");
public static void main(String[] args) throws Exception {
new Thread(unsafeClass).start();
new Thread(safeClass).start();
TimeUnit.SECONDS.sleep(2);
System.out.println("unsafeClass: " + unsafeClass.num);
System.out.println("safeClass: " + safeClass.num);
}
@Override
public void run() {
for(int i = 0 ; i < 10000 ; i++)
{
unsafeClass.num++; //不安全增加
up.incrementAndGet(safeClass); //安全增加(将safeClass包装起来)
}
}
}