java-juc-原子类-AtomicReference和AtomicIntegerFieldUpdater初探

AtomicReference

我们之前已经比较深入的学习了AtomicInteger和AtomicIntegerArray了,现在来看看第三种不同的原子类,AtomicReference。

AtomicReference 基本用法

顾名思义,AtomicReference就是可以对相较基本数据更复杂的对象进行原子操作,例如用户自定义的类等,我们先看看基本用法吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class AtomicReferenceTest {

public static void main(String[] args){

Student s1 = new Student(1);
Student s2 = new Student(2);
AtomicReference<Student> ar = new AtomicReference<>(s1);
ar.compareAndSet(s1, s2);
Student s3 = ar.get();

System.out.println("s3 is " + s3); // 2
System.out.println("s3.equals(s1) = " + s3.equals(s1)); // false
}
}

static class Student {
volatile long id;
public Student(long id) {
this.id = id;
}
public String toString() {
return "student id = "+id;
}
}

用法也很简单,在jdk 1.7及以前,对一个对象的原子性存取的保证,主要是采用compareAndSet方法进行对象的原子操作。

我们先看看构造方法- -

1
2
3
4
5
6
7
8
9
10
11
12
private volatile V value;

public AtomicReference(V initialValue) {
value = initialValue;
}

/**
* Creates a new AtomicReference with null initial value.
*/

public AtomicReference() {

}

和前面讲到的所有的AtomicInteger等类似,AtomicReference也有两个构造方法。一个给定一个初始值传入,一个是初始化一个为null的值。其中value是使用了泛型,因此在构造时需要给AtomicReference指定传入的type。

研究了几次Atomic类后,我们会发现其实操作的套路几乎是一样的。

1
2
3
4
5
6
7
8
9
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}

同样的使用一个Unsafe类来提供CAS操作,使用valueOffset保存value在内存中的地址偏移,便于CAS操作时读取内存中的值。

在无参数的构造方法构造后,可以使用set(newValue)方法继续赋值,也可以用compareAndSet进行原子赋值。

既然说到了compareAndSet,我们还是提一下吧,这个也是用了CAS函数进行原子赋值,方法如下:

1
2
3
4
5
6
7
8
9
10
11
/**
* 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(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

其又调用了native方法:

1
public final native boolean compareAndSwapObject(Object object, long valueOffset, Object expect, Object update);

这里就和AtomicInteger等原子类一样了。同样的方法还有如下:

1
2
3
public final V getAndSet(V newValue) {
return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
}

本质上也是调用了Unsafe类中的compareAndSwapObject方法。

另外,在jdk 1.8中也添加了对lambda表达式的支持,新增了getAndUpdate, updateAndGet等方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final V getAndUpdate(UnaryOperator<V> updateFunction) {
V prev, next;
do {
prev = get();
next = updateFunction.apply(prev);
} while (!compareAndSet(prev, next));
return prev;
}

public final V updateAndGet(UnaryOperator<V> updateFunction) {
V prev, next;
do {
prev = get();
next = updateFunction.apply(prev);
} while (!compareAndSet(prev, next));
return next;
}

大家可以看到其实这里也是调用了compareAndSet方法,只是支持了UnaryOperator,这也是java 8的新特性之一。

好了,关于AtomicReference就介绍到这儿了。

AtomicIntegerFieldUpdater

在juc的原子类包里,还有第四种原子类,就是field updater。顾名思义,通过这个类我们可以对某个对象的integer的成员变量进行原子更新操作。

我们看看基本用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
AtomicIntegerFieldUpdater<Student> mAtoLong = AtomicIntegerFieldUpdater.newUpdater(Student.class, "id");
Student person = new Student(123456);
mAtoLong.compareAndSet(person, 123456, 1000);
System.out.println("id=" + person.toString()); //1000
mAtoLong.getAndAdd(person,10);


static class Student {
volatile int id;

public Student(int id) {
this.id = id;
}

public String toString() {
return "student id = " + id;
}
}

我们可以看到,AtomicIntegerFieldUpdater可以保证更新一个对象中int属性值的原子性。这里的使用方法可能和我们之前分析的三种原子类还不太一样,因为AtomicIntegerFieldUpdater
是一个抽象类,让我们看看源码。

1
2
3
4
5
6
7
8
9
10
11
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}

/**
* Protected do-nothing constructor for use by subclasses.
*/

protected AtomicIntegerFieldUpdater() {

}

先不管其他的修改值的方法,事实上我们在代码里使用时也是走到了newUpdater方法,传进一个类名参数和要修改的属性名(这里是不是有点像反射的感觉?别着急,我们慢慢往下看看),返回了一个AtomicIntegerFieldUpdaterImpl对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 AtomicIntegerFieldUpdaterImpl(final Class<T> tclass, final String fieldName, final Class<?> caller) {
final Field field;
final int modifiers;
try {
field = AccessController.doPrivileged(
new PrivilegedExceptionAction<Field>() {
public Field run() throws NoSuchFieldException {
return tclass.getDeclaredField(fieldName);
}
});
modifiers = field.getModifiers();
sun.reflect.misc.ReflectUtil.ensureMemberAccess(
caller, tclass, null, modifiers);
ClassLoader cl = tclass.getClassLoader();
ClassLoader ccl = caller.getClassLoader();
if ((ccl != null) && (ccl != cl) && ((cl == null) || !isAncestor(cl, ccl))) {
sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
}
} catch (PrivilegedActionException pae) {
throw new RuntimeException(pae.getException());
} catch (Exception ex) {
throw new RuntimeException(ex);
}

Class<?> fieldt = field.getType();
if (fieldt != int.class)
throw new IllegalArgumentException("Must be integer type");

if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");

this.cclass = (Modifier.isProtected(modifiers) && caller != tclass) ? caller : null;
this.tclass = tclass;
offset = unsafe.objectFieldOffset(field);
}

可以看到,我们之前的猜测没有错,传进的类名和field名用来进行反射通过getDeclaredField获取到了这个类的特定的成员域。

在通过反射获取到int属性成员后,检查了调用的原类的安全权限,接着检查了这个field是否是一个int,是否是一个volatile变量,当这一切都没有问题后才通过unsafe类来进行内存地址的获取,
一起后续的一系列原子操作。

see? 又看到unsafe啦,这个真是我们的老朋友了,这里的原子操作也是unsafe提供的CAS函数进行的。

而比较有意思的一点是,在AtomicIntegerFieldUpdater中的compareAndSet也是abstract的,具体的实现在AtomicIntegerFieldUpdaterImpl中。

1
2
3
4
public boolean compareAndSet(T obj, int expect, int update) {
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
return unsafe.compareAndSwapInt(obj, offset, expect, update);
}

我想关于compareAndSet我们也算比较熟悉了,这里就不再详细介绍了。

事实上,在AtomicIntegerFieldUpdaterImpl中的构造方法里取出了对应对象的int field并检查一切没有问题后,接下来的处理就和AtomicInteger的思路以及实现方法一样了。对这个int进行诸如getAndAdd、addAndGet、getAndUpdate和updateAndGet等就是一样的过程,这里也不再细述了。

只不过AtomicIntegerFieldUpdater的特殊性在于有些基本方法是abstract,需要在AtomicIntegerFieldUpdaterImpl中实现,也就是说一些原子操作实际上是在AtomicIntegerFieldUpdaterImpl中完成。

1
2
3
4
5
6
7
8
9
public abstract boolean compareAndSet(T obj, int expect, int update);

public abstract boolean weakCompareAndSet(T obj, int expect, int update);

public abstract void set(T obj, int newValue);

public abstract void lazySet(T obj, int newValue);

public abstract int get(T obj);

这些方法都是在AtomicIntegerFieldUpdaterImpl中进行了实现,本质上还是Unsafe类走原子操作。

另外,像getAndAdd , addAndGet等方法在AtomicIntegerFieldUpdater中和AtomicIntegerFieldUpdaterImpl都有实现,但是本质上都是CAS函数,也和AtomicInteger一样。

像getAndUpdate和updateAndGet是java 8新增的方法,用法也和也和AtomicInteger一样。

总之,AtomicIntegerFieldUpdater自身定义一个abstract类,通过子类实现反射获得对应的属性后,接下来的原子操作就和AtomicInteger等一样啦。这里就不再继续到unsafe中啦。

好了,关于java.util.concurrent中的原子类的包的分析就大概这三篇文章讲一下啦。往后我们就继续学习其他并发知识吧。