在java.util.concurrent.atomic包下面,有许多的原子类,这里面的操作类型大多数与JAVA中基本类型的包装类对应。目的是为了防止高并发的情况下,各个线程操作产生错误数据。这里就通过AtomicInteger这个类进行为大家简单讲解下。
属性介绍
通过源码我们发现。AtomicInteger中只有三个属性,一个
unsafe对象属性,一个valueOffset属性以及一个被volatile修饰的value属性。
1.unsafe属性可以看到是由Unsafe类调用其静态方法生成。
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
Unsafe类提供了硬件级别的原子操作。里面有许多的本地方法。JDK中的一些无锁并发操作类都是基于它,里面最主要的就是CAS相关操作。这块我就不详细说明了(有兴趣的小伙伴可以去了解下)。
2.valueOffset属性是当前类中值value对应的内存起始地址。通过当前类中的静态代码块可以看出,他的赋值情况:
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
他是通过unsafe获取到当前类中的属性的起始地址。来进行赋值。点击到下面可以看到调用的objectFieldOffset的native方法进行调用获取。
3.value属性就是当前类中的值,这里也就是我们所需要的值。这里可以从外部传递过来。
public AtomicInteger(int initialValue) {
value = initialValue;
}
主要方法介绍
由当前的类型,我们就能了解到这是一个原子类相关操作,即每一个操作不需要考虑多线程并发的问题。里面的方法如下:
刚开始的构造函数以及get和set方法在这里就不做说明了。
1.lazySet(int)这个方法字面上说是懒设值,看到对应方法上面的注解说。这个会最终设值(还是有点懵。。)。于是点击进去查看对应调用的方法:
public native void putOrderedInt(Object var1, long var2, int var4);
看到里面调用的是unsafe的方法,putOrderedInt方法是putIntVolatile方法的延迟实现,不保证值的改变被其他线程立即看到。同时,unsafe里面还有对应的putVolatile方法,改方法能将改动的值立即刷新到内存中。以及存在普通的putInt方法,该方法不能保证立即刷新到内存中。
2.getAndSet(int newValue)方法是为了设置值,里面就用到了value的内存地址进行设值。
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
3.compareAndSet(int expect, int update)这个方法就是基于CAS实现的更新数值。
通过无无锁将多线程设置值的问题解决。如果设置成功返回true,反之没设置成功。同时这里也为其他操作提供了一个准确的保证。
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
4.weakCompareAndSet(int expect, int update)方法是针对于当前的数值交换情况。将这个成员变量更新为给定的更新后的值(update)如果当前值等于期望值(expect)时。
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
相信一些小伙伴对此方法有点疑惑,因为如果是基于CAS交换的话,没必要增加一个weak在方法名上,(同时让我们想起来JAVA中的四种引用类型。。),这里官方就给出了解释:
weakCompareAndSet底层不会创建任何happen-before的保证,也就是不会对volatile字段操作的前后加入内存屏障。因为就无法保证多线程操作下对除了weakCompareAndSet操作的目标变量( 该目标变量一定是一个volatile变量 )之其他的变量读取和写入数据的正确性。
5.getAndUpdate(IntUnaryOperator updateFunction)这个方法先不多说,直接上代码:
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return prev;
}
这里面是使用了循环进行更新值,利用的是之前的CAS操作判断之前的值与之后的值是否是一致的关系。然后进行更新操作。这里还使用了功能型接口IntUnaryOperator。
总体来说:AtomicXXX相关的类的操作都是大致相同的,都是使用的是无锁的CAS操作,对于性能方面能够有所提高,也不需要考虑线程阻塞的情况发生。对于多线程操作的情况下还是很好的一个选择。