李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
36.并发编程之字段更新器
Leefs
2022-11-08 PM
1212℃
0条
[TOC] ### 一、概述 前面所讲的几个原子更新引用类型如:`AtomicReference`,用于整个对象的更新。但不是每次都必须更新整个对象,有可能我们只需对对象中的某个字段进行原子性修改时,那么就需要使用原子更新字段类。 在`java.util.concurrent.atomic`中,原子类型字段更新器有以下三种: - `AtomicIntegerFieldUpdater`:基于反射的工具类,可以原子性的更新指定对象的指定int类型字段。 - `AtomicLongFieldUpdater`:基于反射的工具类,可以原子性的更新指定对象的指定long类型字段。 - `AtomicReferenceFieldUpdater`:基于反射的工具类,可以原子性的更新指定对象的指定应用类型字段。 ### 二、使用条件 原子类型字段更新器在内部通过Unsafe类的native方法保证操作的原子性。 关于原子类型字段更新器的使用需要注意以下几个方面: - 字段必须是volatile类型的,用于保证可见性。 - 字段和字段更新器的访问类型(public/protected/private)必须一致。 - 字段只能是实例变量,不能是类变量(static)。 - 字段不能是final的变量,这样的字段不可修改。 - 如果要处理Integer和Long类型,则需要使用`AtomicReferenceFieldUpdater`。 + 属性必须对当前的Updater所在的区域是可见的,如果不是当前类内部进行原子更新器操作不能使用private,protected子类操作父类时修饰符必须是protect权限及以上,如果在同一个package下则必须是default权限及以上,也就是说无论何时都应该保证操作类与被操作类间的可见性。 + 对于 `AtomicIntegerFieldUpdate` 和 `AtomicLongFieldUpdate` 只能修改 int/long 类型的字段,不能修改包装类型。如果要修改包装类型就需要使用 `AtomicReferenceFieldUpdate`。 **示例** 利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 volatile 修饰的字段使用,否则会出现异常 ```java import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class Test05 { private volatile int field; public static void main(String[] args) { AtomicIntegerFieldUpdater fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Test05.class, "field"); Test05 test5 = new Test05(); fieldUpdater.compareAndSet(test5, 0, 10); // 修改成功 field = 10 System.out.println(test5.field); // 修改成功 field = 20 fieldUpdater.compareAndSet(test5, 10, 20); System.out.println(test5.field); // 修改失败 field = 20 fieldUpdater.compareAndSet(test5, 10, 30); System.out.println(test5.field); } } ``` **运行结果** ``` 10 20 20 ``` ### 三、主要方法 ##### 三者共有的方法 | 方法 | 说明 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | `static
AtomicIntFieldUpdater
newUpdater(Class
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` 的其他调用,此方法可以确保原子性,但对于字段中的其他更改则不一定确保原子性。 ```java compareAndSet(T obj, V expect, V update) ``` **参数**: + obj - 有条件地设置其字段的对象 + expect - 预期值 + update - 新值 **返回**:如果成功,则返回 true ##### 比较设置方法weakCompareAndSet `weakCompareAndSet()方法`: 如果当前值 == 预期值,则以原子方式将此更新程序所管理的给定对象的字段值设置为给定的更新值。 对 `compareAndSet` 和 set 的其他调用,此方法可以确保原子性,但对于字段中的其他更改则不一定确保原子性,并且可能会意外失败。 ```java weakCompareAndSet(T obj, V expect, V update) ``` **参数**: + obj - 有条件地设置其字段的对象 + expect - 预期值 + update - 新值 **返回**:如果成功,则返回 true。 ### 四、使用 #### 4.1 实例的创建 + **构造方法** `AtomicReferenceFieldUpdater` 有一个 protected 的无参数构造方法,只能供子类使用。所以一般情况下创建一个 `AtomicReferenceFieldUpdater` 实例需要使用该类提供的一个静态方法 `newUpdater`。 ```java // 受保护的无操作构造方法,供子类使用。 protected AtomicReferenceFieldUpdater() ``` + **创建实例的静态方法newUpdater** `AtomicReferenceFieldUpdater` 可以为一个用于更新指定类的声明为volatile类型的属性进行原子性更新,通过调用 `AtomicReferenceFieldUpdater` 的静态方法 `newUpdater` 创建实例。 ```java public static
AtomicReferenceFieldUpdater
newUpdater(Class
tclass, Class
vclass, String fieldName) ``` **参数**: + `tclass`:包含要更新属性/字段的类的类型,即需要更新字段所在的class类。 + `vclass` : 更新属性/字段所属的类型。 + `fieldName` :更新属性/字段的名称。 **返回**:更新程序 **抛出**: + `IllegalArgumentException` - 如果该字段不是可变引用类型。 + `RuntimeException` - 如果该类不保持字段,或者是错误的类型,将抛出 `RuntimeException` 和一个嵌套的基于反射的异常。 #### 4.2 方法使用 ```java import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; /** * AtomicReferenceFieldUpdater的测试类 */ @Slf4j public class AtomicReferenceFieldUpdaterTest { //abstract private volatile Integer age; /** * 初始化采用静态方法+内部类,私有化构造函数 * @throws */ @Test public void testNewUpdater() { AtomicReferenceFieldUpdaterTest test=new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); System.out.println(updater.get(test)); } /** * 如果指定对象的目标属性值为expect的值,则更新成新值,返回true * 否则不更新,返回false * @throws */ @Test public void testCompareAndSet() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); System.out.println(updater.compareAndSet(test, null, 2)); System.out.println(updater.get(test)); } /** * 如果指定对象的目标属性值为expect的值,则更新成新值,返回true * 否则不更新,返回false * weakCompareAndSet和compareAndSet暂时未发现区别 * @throws */ @Test public void testWeakCompareAndSet() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); System.out.println(updater.weakCompareAndSet(test, null, 2)); System.out.println(updater.get(test)); } /** * 设置指定对象的目标属性值为新的值,unsafe.putIntVolatile设置值 * @throws */ @Test public void testSet() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); updater.set(test, 22); System.out.println(updater.get(test)); } /** * 设置指定对象的目标属性值为新的值,unsafe.putOrderedInt * @throws */ @Test public void testLazySet() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); updater.lazySet(test, 22); System.out.println(updater.get(test)); } /** * 获取指定对象的目标属性值,需要检查是否越界 * @throws */ @Test public void testGet() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); System.out.println(updater.get(test)); } /** * 返回指定对象的目标属性值,并设置成新的值 * @throws */ @Test public void testGetAndSet() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); System.out.println(updater.getAndSet(test, 32)); System.out.println(updater.get(test)); } /** * 返回指定对象的目标属性值,并更新成新值 * @throws */ @Test public void testGetAndUpdate() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); UnaryOperator operator = new UnaryOperator() { @Override public Object apply(Object o) { return new Integer(333); } }; System.out.println(updater.getAndUpdate(test, operator)); System.out.println(updater.get(test)); } /** * 更新成新值并返回指定对象的目标属性值 * @throws */ @Test public void testUpdateAndGet() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); UnaryOperator operator = new UnaryOperator() { @Override public Object apply(Object o) { return new Integer(333); } }; System.out.println(updater.updateAndGet(test, operator)); System.out.println(updater.get(test)); } /** * 返回指定对象的目标属性值,并更新成新值 * @throws */ @Test public void testGetAndAccumulate() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); BinaryOperator operator = new BinaryOperator() { @Override public Object apply(Object o, Object o2) { return o2; } }; System.out.println(updater.getAndAccumulate(test, 2, operator)); System.out.println(updater.get(test)); } /** * 更新成新值并返回指定对象的目标属性值 * @throws */ @Test public void testAccumulateAndGet() { AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest(); AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, Integer.class,"age"); BinaryOperator operator = new BinaryOperator() { @Override public Object apply(Object o, Object o2) { return o2; } }; System.out.println(updater.accumulateAndGet(test, 2, operator)); System.out.println(updater.get(test)); } } ``` *附参考文章链接* *https://www.kongzid.com/archives/java22*
标签:
并发编程
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2554.html
上一篇
35.并发编程之原子数组
下一篇
37.并发编程之LongAdder介绍
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
pytorch
机器学习
Flume
JavaWEB项目搭建
字符串
人工智能
容器深入研究
MyBatisX
Elasticsearch
JVM
Shiro
GET和POST
锁
SpringBoot
Git
Kibana
Livy
CentOS
算法
Ubuntu
数据结构和算法
HDFS
递归
BurpSuite
国产数据库改造
Python
Quartz
Java编程思想
Spark
MyBatis-Plus
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭