原子类之字段更新器

一、概述

前面所讲的几个原子更新引用类型如:AtomicReference,用于整个对象的更新。但不是每次都必须更新整个对象,有可能我们只需对对象中的某个字段进行原子性修改时,那么就需要使用原子更新字段类。

java.util.concurrent.atomic中,原子类型字段更新器有以下三种:

  • AtomicIntegerFieldUpdater:基于反射的工具类,可以原子性的更新指定对象的指定int类型字段。
  • AtomicLongFieldUpdater:基于反射的工具类,可以原子性的更新指定对象的指定long类型字段。
  • AtomicReferenceFieldUpdater:基于反射的工具类,可以原子性的更新指定对象的指定应用类型字段。

二、使用条件

原子类型字段更新器在内部通过Unsafe类的native方法保证操作的原子性。

关于原子类型字段更新器的使用需要注意以下几个方面:

  • 字段必须是volatile类型的,用于保证可见性。

  • 字段和字段更新器的访问类型(public/protected/private)必须一致。

  • 字段只能是实例变量,不能是类变量(static)。

  • 字段不能是final的变量,这样的字段不可修改。

  • 如果要处理Integer和Long类型,则需要使用AtomicReferenceFieldUpdater

  • 属性必须对当前的Updater所在的区域是可见的。

  • 对于 AtomicIntegerFieldUpdateAtomicLongFieldUpdate 只能修改 int/long 类型的字段,不能修改包装类型。如果要修改包装类型就需要使用 AtomicReferenceFieldUpdate

示例

利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 volatile 修饰的字段使用,否则会出现异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
}
}

运行结果

1
2
3
10
20
20

三、主要方法

三者共有的方法
方法 说明
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) 如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段设置为给定的更新值
AtomicIntegerFieldUpdaterAtomicLongFieldUpdater的独有方法:
方法 说明
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()方法: 如果当前值 == 预期值,则以原子方式将此更新程序所管理的给定对象的字段值设置为给定的更新值。

compareAndSetset 的其他调用,此方法可以确保原子性,但对于字段中的其他更改则不一定确保原子性。

1
compareAndSet(T obj, V expect, V update)

参数

  • obj - 有条件地设置其字段的对象
  • expect - 预期值
  • update - 新值

返回:如果成功,则返回 true

比较设置方法weakCompareAndSet

weakCompareAndSet()方法: 如果当前值 == 预期值,则以原子方式将此更新程序所管理的给定对象的字段值设置为给定的更新值。

compareAndSet 和 set 的其他调用,此方法可以确保原子性,但对于字段中的其他更改则不一定确保原子性,并且可能会意外失败。

1
weakCompareAndSet(T obj, V expect, V update)

参数

  • obj - 有条件地设置其字段的对象
  • expect - 预期值
  • update - 新值

返回:如果成功,则返回 true。

四、使用

4.1 实例的创建

  • 构造方法

AtomicReferenceFieldUpdater 有一个 protected 的无参数构造方法,只能供子类使用。所以一般情况下创建一个 AtomicReferenceFieldUpdater 实例需要使用该类提供的一个静态方法 newUpdater

1
2
// 受保护的无操作构造方法,供子类使用。
protected AtomicReferenceFieldUpdater()
  • 创建实例的静态方法newUpdater

AtomicReferenceFieldUpdater 可以为一个用于更新指定类的声明为volatile类型的属性进行原子性更新,通过调用 AtomicReferenceFieldUpdater 的静态方法 newUpdater 创建实例。

1
2
3
public static <U,W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass,
Class<W> vclass,
String fieldName)

参数

  • tclass:包含要更新属性/字段的类的类型,即需要更新字段所在的class类。
  • vclass : 更新属性/字段所属的类型。
  • fieldName :更新属性/字段的名称。

返回:更新程序

抛出

  • IllegalArgumentException - 如果该字段不是可变引用类型。
  • RuntimeException - 如果该类不保持字段,或者是错误的类型,将抛出 RuntimeException 和一个嵌套的基于反射的异常。

4.2 方法使用

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package site.weiyikai.demo06;

import org.junit.Test;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;

/**
* @author xiaowei
* @create 2023-03-29 19:09
*/
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)); //null
}

/**
* 如果指定对象的目标属性值为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));
}
}

原子类之字段更新器
http://example.com/2023/03/29/原子类之字段更新器/
作者
程序员小魏
发布于
2023年3月29日
许可协议