AtomicReference和AtomicReferenceFieldUpdater有何异同?
一、面试官视角:这道题想考察什么?
- 是否熟练掌握原子操作的概念 - 
- 线程安全问题涉及到操作原子性的概念 
 
- 是否熟悉AR和ARFU这两个类的用法和原理 
- 是否对Java对象的内存占用有认识 
- 是否有较强的敏感度和深入探究的精神 
二、题目剖析:
1、AtomicReference的用法
| 12
 3
 
 | class AtomicReferenceValueHolder {AtomicReference<String> atomicValue = new AtomicReference<>("HelloAtomic");
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 |   AtomicReferenceValueHolder holder = new AtomicReferenceValueHolder();
 
 holder.atomicValue.compareAndSet("Hello", "World");
 String value = holder.atomicValue.getAndUpdate(new UnaryOperator<String>(){
 @Override
 public String apply(String s) {
 return "HelloWorld";
 }
 });
 
 | 
  2、AtomicReferenceFieldUpdater的用法
  | 12
 3
 4
 
 | class SimpleValueHolder {public static AtomicReferenceFieldUpdater<SimpleValueHolder, String> valueUpdater = AtomicReferenceFieldUpdater.newUpdater(SimpleValueHolder.class, String.class, "value");
 volatile String value = "HelloAtomic";
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 
 |   SimpleValueHolder holder = new SimpleValueHolder();SimpleValueHolder.valueUpdater.compareAndSet(holder, "Hello", "World");
 String value = SimpleValueHolder.valueUpdater.getAndUpdate(holder, new UnaryOperator<String>(){
 @Override
 public String apply(String s) {
 return "HelloWorld";
 }
 });
 
 | 
3、AR和ARFU的对比
  | 12
 3
 
 | class AtomicReferenceValueHolder {AtomicReference<String> atomicValue = new AtomicReference<>("HelloAtomic");
 }
 
 | 
| 12
 3
 4
 
 | class SimpleValueHolder {public static AtomicReferenceFieldUpdater<SimpleValueHolder, String> valueUpdater = AtomicReferenceFieldUpdater.newUpdater(SimpleValueHolder.class, String.class, "value");
 volatile String value = "HelloAtomic";
 }
 
 | 
发现AR比ARFU写起来的时候代码量要少得多,而且AR不用像ARFU一样需要用到反射,但是很多框架却选择使用ARFU,我们查看源码发现,AR源码里面,本质也有一个private volatile V value; 存在,那么这两者的差异点主要在于AR本身是要指向一个对象的,也就是要比ARFU多创建一个对象,而这个对象的头(Header)占12个字节,它的成员(Fields)占4个字节,也就比ARFU要多出来16个字节,这是对于32位的是这种情况,如果是64位的话,你启用了-XX:+UseComparessedOops 指针压缩的话,那么Header还是占用12个字节,Fields也还是占用4个字节,但如果没有启用指针压缩的话,那么Header是占16个字节,Fields占用8个字节,总共占用24个字节,那么就说明每创建一个AR都会多出来这么多的内存,那么对GC的压力就有很大的影响了。
  4、迁移:使用ARFU的例子(1)
  | 12
 3
 4
 5
 
 | public class BufferedInputSteam extends FilterInputStream {protected volatile byte[];
 
 private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater(BufferedInputStream.class, byte[].class, "buf");
 }
 
 | 
  5、迁移:使用ARFU的例子(2)
  | 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | val value by lazy(LazyThreadSafetyMode.PUBLICATION) {
 ...
 }
 
 
 private class SafePublicationLazyImpl<out T>(initializer:() -> T):Lazy<T>,Serializable {
 override val value:T
 get() {
 ...
 val newValue = initializerValue()
 if(valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
 ...
 }
 ...
 }
 companion object {
 private val valueUpdater = AtomicReferenceFieldUpdater.newUpdater(...)
 }
 }
 
 | 
三、题目结论:
- AR和ARFU的功能一致,原理相同,都是基于Unsafe的CAS操作 
- AR通常作为对象的成员使用,占16B(指针压缩)或24B(指针不压缩),取决于多少位的虚拟机以及有没有开启指针压缩 
- ARFU通常作为类的静态成员使用,对实例成员进行修改,这样做的作用就是节省内存 
- AR使用更友好,ARFU更适合类实例比较多的场景(比如写网络框架的时候肯定会用到ARFU) 
- Kotlin协程的实现,因为也是需要对这个结果保证操作原子性的,所以它的实现也用到了ARFU