AtomicReference与AtomicReferenceFieldUpdater的使用

前言:

关于JDK提供的原子类,我们之前经常使用的就是基础类型的相关原子类,如AtomicInteger、AtomicLong、AtomicBoolean等。那么有没有关于对象类型的原子类呢,确实是有的,就是AtomicReference类。

笔者在学习Netty时,发现了一个更好玩的类AtomicReferenceFieldUpdater,可以对对象的属性实现原子修改。我们一起来学习下。

1.AtomicReference

1.1 基础使用

有关于其使用还是比较简单的,示例如下:

AtomicReference<String> reference = new AtomicReference<String>();
// 设置初始值
reference.set("test1");
// 原子性的将值从test1设置为test2
boolean isOk = reference.compareAndSet("test1", "test2");

基于以上示例,我们也可以进行对象的原子修改

AtomicReference<Date> reference = new AtomicReference<Date>();
// 设置初始值
Date date1 = new Date();
reference.set(date1);
// 需要的修改后的值
Date date2 = new Date();

boolean isOk = reference.compareAndSet(date1, date2);

1.2 源码释义

public class AtomicReference<V> implements java.io.Serializable {
 
    // Unsafe对象
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // 用于获取某个字段相对Java对象的起始地址的偏移量
    private static final long valueOffset;

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

    // 所包装的value值
    private volatile V value;
    // 初始化value值
    public AtomicReference(V initialValue) {
        value = initialValue;
    }
    
    // 本质上还是通过调用Unsafe来实现的
    public final boolean compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }
    ...
}

很明显可以看到,其原子功能是通过Unsafe来实现的。更多关于Unsafe的知识点,读者可以自行查找。

2.AtomicReferenceFieldUpdater

既然已经有了AtomicReference实现对象的原子变更,那么AtomicReferenceFieldUpdater的具体作用是什么呢?

如果我们期望对对象的某个属性进行原子变更时,则可以使用其来实现。

我们先来看一下示例:

// 创建对Person对象的name属性的原子变更类
AtomicReferenceFieldUpdater<Person, String> fieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Person.class, String.class, "name");

Person person = new Person();
fieldUpdater.set(person, "jack");

// 原子性的将name属性将原来的jack转变为lucy
boolean b = fieldUpdater.compareAndSet(person, "jack", "lucy");
String s = fieldUpdater.get(person);
Assert.assertEquals("lucy", s);

public class Person {
    volatile String name;
    volatile int age;
}

使用过程并不复杂,就是创建AtomicReferenceFieldUpdater时可能有点难度,newUpdater()方法的三个属性分别是:对象类型、修改的属性类型、修改的属性名称;

可以再来看下netty中关于其使用

private static final class DefaultResourceLeak<T> extends WeakReference<Object> implements ResourceLeakTracker<T>, ResourceLeak {
    // 实现对DefaultResourceLeak.head属性的原子修改
    private static final AtomicReferenceFieldUpdater<DefaultResourceLeak<?>, TraceRecord> headUpdater =
        (AtomicReferenceFieldUpdater)
        AtomicReferenceFieldUpdater.newUpdater(DefaultResourceLeak.class, TraceRecord.class, "head");

    // 如果属性类型为Integer,那么可以直接使用AtomicIntegerFieldUpdater进行修改
    private static final AtomicIntegerFieldUpdater<DefaultResourceLeak<?>> droppedRecordsUpdater =
        (AtomicIntegerFieldUpdater)
        AtomicIntegerFieldUpdater.newUpdater(DefaultResourceLeak.class, "droppedRecords");

    @SuppressWarnings("unused")
    private volatile TraceRecord head;
    
}

使用还是比较简单的,那么使用的时候有没有哪些需要注意的点呢?

操作的字段不能是static类型;
操作的字段不能是final类型的;
字段必须是volatile修饰的,也就是数据本身是读一致的;
属性必须对当前的Updater所在的区域是可见的,如果不是当前类内部进行原子更新器操作不能使用private,protected子类操作父类时修饰符必须是protect权限及以上,
如果在同一个package下则必须是default权限及以上,也就是说无论何时都应该保证操作类与被操作类间的可见性;

总结:

最后一个问题留给读者,既然我们想实现对对象的属性原子性修改,那直接在setxxx属性方法中,对其进行加锁处理,不也是一样的吗,为什么要这么麻烦呢?

Guess you like

Origin blog.csdn.net/qq_26323323/article/details/121185595