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); System.out.println("s3.equals(s1) = " + s3.equals(s1)); } } 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()); 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中的原子类的包的分析就大概这三篇文章讲一下啦。往后我们就继续学习其他并发知识吧。