原子类之字段更新器
一、概述
前面所讲的几个原子更新引用类型如:AtomicReference
,用于整个对象的更新。但不是每次都必须更新整个对象,有可能我们只需对对象中的某个字段进行原子性修改时,那么就需要使用原子更新字段类。
在java.util.concurrent.atomic
中,原子类型字段更新器有以下三种:
AtomicIntegerFieldUpdater
:基于反射的工具类,可以原子性的更新指定对象的指定int类型字段。AtomicLongFieldUpdater
:基于反射的工具类,可以原子性的更新指定对象的指定long类型字段。AtomicReferenceFieldUpdater
:基于反射的工具类,可以原子性的更新指定对象的指定应用类型字段。
二、使用条件
原子类型字段更新器在内部通过Unsafe类的native方法保证操作的原子性。
关于原子类型字段更新器的使用需要注意以下几个方面:
字段必须是volatile类型的,用于保证可见性。
字段和字段更新器的访问类型(public/protected/private)必须一致。
字段只能是实例变量,不能是类变量(static)。
字段不能是final的变量,这样的字段不可修改。
如果要处理Integer和Long类型,则需要使用
AtomicReferenceFieldUpdater
。属性必须对当前的Updater所在的区域是可见的。
对于
AtomicIntegerFieldUpdate
和AtomicLongFieldUpdate
只能修改 int/long 类型的字段,不能修改包装类型。如果要修改包装类型就需要使用AtomicReferenceFieldUpdate
。
示例
利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 volatile 修饰的字段使用,否则会出现异常
1 |
|
运行结果
1 |
|
三、主要方法
三者共有的方法
方法 | 说明 |
---|---|
static <U> AtomicIntFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) |
原子类型字段更新器提供的一个静态泛型方法,用于创建和返回指定字段和指定类型的原子类型字段更新器实例对象 |
abstract V get(T obj) |
获取此更新器管理的在给定对象的字段中保持的当前值 |
abstract void set(T obj, int newValue) |
将此更新器管理的给定对象的字段设置为给定更新值 |
abstract void lazySet(T obj, int newValue) |
最后将此更新器管理的给定对象的字段设置为给定更新值 |
int getAndSet(T obj, int newValue) |
将此更新器管理的给定对象的字段以原子方式设置为给定值,并返回旧值 |
abstract boolean compareAndSet(T obj, int expect, int update) |
如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段设置为给定的更新值 |
abstract boolean weakCompareAndSet(T obj, int expect, int update) |
如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段设置为给定的更新值 |
AtomicIntegerFieldUpdater
和AtomicLongFieldUpdater
的独有方法:
方法 | 说明 |
---|---|
int addAndGet(T obj, int delta) |
以原子的方式将给定值和更新器管理的给定对象的当前值相加,并返回相加后的值 |
int incrementAndGet(T obj) |
以原子方式将此更新器管理的给定对象字段当前值加 1 |
int decrementAndGet(T obj) |
以原子方式将此更新器管理的给定对象字段当前值减1 |
int getAndIncrement(T obj) |
以原子方式将此更新器管理的给定对象字段的当前值加 1 |
int getAndAdd(T obj, int delta) |
以原子方式将给定值添加到此更新器管理的给定对象的字段的当前值 |
int getAndDecrement(T obj) |
以原子方式将此更新器管理的给定对象字段当前值减 1 |
lazySet与set实现的功能类似,二者区别如下:
lazySet
实现的是最终一致性,set实现的是强一致性lazySet
多线程并发时,线程A调用unsafe.putOrderedInt
更新newValue
值时,只是把线程A内存中的元素更新成功了,但其它线程此时看不到线程A更新的newValue
值,需过一会,线程A更新的newValue
才会刷入到主内存中,此时其它线程才能看到线程A更新的值- set方法在多线程并发时,线程A更新的值会立即刷入主内存中
总结:对于数据一致性要求高的建议用set
比较设置方法compareAndSet
compareAndSet()方法
: 如果当前值 == 预期值,则以原子方式将此更新程序所管理的给定对象的字段值设置为给定的更新值。
对 compareAndSet
和 set
的其他调用,此方法可以确保原子性,但对于字段中的其他更改则不一定确保原子性。
1 |
|
参数:
- obj - 有条件地设置其字段的对象
- expect - 预期值
- update - 新值
返回:如果成功,则返回 true
比较设置方法weakCompareAndSet
weakCompareAndSet()方法
: 如果当前值 == 预期值,则以原子方式将此更新程序所管理的给定对象的字段值设置为给定的更新值。
对 compareAndSet
和 set 的其他调用,此方法可以确保原子性,但对于字段中的其他更改则不一定确保原子性,并且可能会意外失败。
1 |
|
参数:
- obj - 有条件地设置其字段的对象
- expect - 预期值
- update - 新值
返回:如果成功,则返回 true。
四、使用
4.1 实例的创建
- 构造方法
AtomicReferenceFieldUpdater
有一个 protected 的无参数构造方法,只能供子类使用。所以一般情况下创建一个 AtomicReferenceFieldUpdater
实例需要使用该类提供的一个静态方法 newUpdater
。
1 |
|
- 创建实例的静态方法newUpdater
AtomicReferenceFieldUpdater
可以为一个用于更新指定类的声明为volatile类型的属性进行原子性更新,通过调用 AtomicReferenceFieldUpdater
的静态方法 newUpdater
创建实例。
1 |
|
参数:
tclass
:包含要更新属性/字段的类的类型,即需要更新字段所在的class类。vclass
: 更新属性/字段所属的类型。fieldName
:更新属性/字段的名称。
返回:更新程序
抛出:
IllegalArgumentException
- 如果该字段不是可变引用类型。RuntimeException
- 如果该类不保持字段,或者是错误的类型,将抛出RuntimeException
和一个嵌套的基于反射的异常。
4.2 方法使用
1 |
|