Introduction
Atomic
means that an operation is uninterruptible. Even when multiple threads are executing together, once an operation is started, it is not interrupted by other threads.
So: atomic classes are classes that have the characteristics of atomic/atomic operations.
The atomic classes of the concurrency package JUC(java.util.concurrent)
are all in java.util.concurrent.atomic
.
Example
Run the following code.
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
|
public class AtomicTest {
@Test
void test() {
Date date = new Date();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
date.volatileNumAdd();
date.atomicNumAdd();
}
}, i + "").start();
}
while (Thread.activeCount() > 2) {
// 让出当前线程,给其他线程执行
Thread.yield();
}
System.out.println("over! volatile:\t" + date.volatileNum);
System.out.println("over! atomic:\t" + date.atomicNum);
}
}
class Date {
volatile int volatileNum = 0;
AtomicInteger atomicNum = new AtomicInteger(0);
public void volatileNumAdd() {
this.volatileNum++;
}
public void atomicNumAdd() {
atomicNum.getAndIncrement();
}
}
|
The results are as follows.
1
2
|
main over! volatileNum : 7111
main over! atomicNum : 10000
|
Classification
- Basic types : update basic types using atomic
AtomicInteger
: Atomic class for integers
AtomicLong
: Long integer atomic class
AtomicBoolean
: the boolean atomic class
- Array type : updates an element in an array using atomic methods
AtomicIntegerArray
: the atomic class for integer arrays
AtomicLongArray
: Atomic class for long integer arrays
AtomicReferenceArray
: the atomic class for reference type arrays
- Reference types : for wrapping objects
AtomicReference
: atomic class for reference types
AtomicMarkableReference
: Atomic update of reference types with markers. This class associates a boolean
marker with a reference
AtomicStampedReference
: Atomic update of reference types with version numbers. This class associates integer values with references and can be used to resolve atomic updates to data and version numbers of data, and can solve the ABA
problem that can occur when using CAS
for atomic updates
- Property modification types for objects
AtomicIntegerFieldUpdater
: updater for atomic updates of integer fields
AtomicLongFieldUpdater
: updater for atomic updates of long integer fields
AtomicReferenceFieldUpdater
: Atomic update of fields in reference types
Common methods
Atomic classes of basic types (take AtomicInteger
as an example)
Code
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
|
public class AtomicIntegerTest {
@Test
void test() {
// 无参构造:默认值为0
// AtomicInteger atomic = new AtomicInteger();
// 有参构造:初始化传入初始值,
AtomicInteger atomic = new AtomicInteger(1);
System.out.println("获取当前值:" + atomic.get());
System.out.println("\n设置值");
atomic.set(5);
System.out.println("新值:" + atomic.get());
System.out.println("\n获取当前值,并设置新值");
System.out.println("原始:" + atomic.getAndSet(2));
System.out.println("新值:" + atomic.get());
System.out.println("\n获取当前值,并增加值");
System.out.println("原始:" + atomic.getAndAdd(2));
System.out.println("新值:" + atomic.get());
System.out.println("\n增加值,并获取结果");
System.out.println("新值:" + atomic.addAndGet(2));
System.out.println("\n获取当前值,并自增");
System.out.println("原始:" + atomic.getAndIncrement());
System.out.println("新值:" + atomic.get());
System.out.println("\n获取当前值,并自减");
System.out.println("原始:" + atomic.getAndDecrement());
System.out.println("新值:" + atomic.get());
System.out.println("\n自增后,获取当前值,相当于 ++i");
System.out.println("新值:" + atomic.incrementAndGet());
System.out.println("\n自减后,获取当前值,相当于 --i");
System.out.println("新值:" + atomic.decrementAndGet());
System.out.println("\n比较并修改值");
int i = atomic.get();
System.out.println("原始:" + i);
System.out.println("修改结果1:" + atomic.compareAndSet(i, 8) + "\t值:" + atomic.get());
System.out.println("修改结果2:" + atomic.compareAndSet(i, 8) + "\t值:" + atomic.get());
System.out.println("\n转换");
System.out.println(atomic.intValue());
System.out.println(atomic.longValue());
System.out.println(atomic.floatValue());
System.out.println(atomic.doubleValue());
}
}
|
The output is as follows
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
|
获取当前值:1
设置值
新值:5
获取当前值,并设置新值
原始:5
新值:2
获取当前值,并增加值
原始:2
新值:4
增加值,并获取结果
新值:6
获取当前值,并自增
原始:6
新值:7
获取当前值,并自减
原始:7
新值:6
自增后,获取当前值,相当于 ++i
新值:7
自减后,获取当前值,相当于 --i
新值:6
比较并修改值
原始:6
修改结果1:true 值:8
修改结果2:false 值:8
转换
8
8
8.0
8.0
|
Atomic classes for array types (as AtomicIntegerArray
for example)
Code
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
|
public class AtomicIntegerArrayTest {
@Test
void test() {
// 有参构造:指定数组长度,数组内初始值为0
// AtomicIntegerArray atomic = new AtomicIntegerArray(10);
// 有参构造:传入数组
AtomicIntegerArray atomic = new AtomicIntegerArray(new int[] {1, 2, 3, 4, 5});
System.out.println("数组的长度为:" + atomic.length());
System.out.println("\n获取指定位置的值");
for (int i = 0; i < atomic.length(); i++) {
System.out.println(i + "\t" + atomic.get(i));
}
System.out.println("\n更新指定位置值");
atomic.set(0, 2);
System.out.println("位置:" + 0 + "\t新值:" + atomic.get(0));
System.out.println("\n获取指定位置当前值,并设置新值");
System.out.println("位置:" + 0 + "\t原始:" + atomic.getAndSet(0, 2));
System.out.println("位置:" + 0 + "\t新值:" + atomic.get(0));
System.out.println("\n获取指定位置当前值,并增加值");
System.out.println("位置:" + 0 + "\t原始:" + atomic.getAndAdd(0, 2));
System.out.println("位置:" + 0 + "\t新值:" + atomic.get(0));
System.out.println("\n指定位置增加值,并获取结果");
System.out.println("位置:" + 0 + "\t新值:" + atomic.addAndGet(0, 2));
System.out.println("\n获取指定位置当前值,并自增");
System.out.println("位置:" + 0 + "\t原始:" + atomic.getAndIncrement(0));
System.out.println("位置:" + 0 + "\t新值:" + atomic.get(0));
System.out.println("\n获取指定位置当前值,并自减");
System.out.println("位置:" + 0 + "\t原始:" + atomic.getAndDecrement(0));
System.out.println("位置:" + 0 + "\t新值:" + atomic.get(0));
System.out.println("\n指定位置自增后,获取当前值,相当于 ++i");
System.out.println("位置:" + 0 + "\t新值:" + atomic.incrementAndGet(0));
System.out.println("\n指定位置自减后,获取当前值,相当于 --i");
System.out.println("位置:" + 0 + "\t新值:" + atomic.decrementAndGet(0));
System.out.println("\n指定位置比较并修改值");
int i = atomic.get(0);
System.out.println("位置:" + 0 + "\t原始:" + i);
System.out.println("位置:" + 0 + "\t修改结果1:" + atomic.compareAndSet(0, i, 8) + "\t值:" + atomic.get(0));
System.out.println("位置:" + 0 + "\t修改结果2:" + atomic.compareAndSet(0, i, 8) + "\t值:" + atomic.get(0));
}
}
|
Output.
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
|
数组的长度为:5
获取指定位置的值
0 1
1 2
2 3
3 4
4 5
更新指定位置值
位置:0 新值:2
获取指定位置当前值,并设置新值
位置:0 原始:2
位置:0 新值:2
获取指定位置当前值,并增加值
位置:0 原始:2
位置:0 新值:4
指定位置增加值,并获取结果
位置:0 新值:6
获取指定位置当前值,并自增
位置:0 原始:6
位置:0 新值:7
获取指定位置当前值,并自减
位置:0 原始:7
位置:0 新值:6
指定位置自增后,获取当前值,相当于 ++i
位置:0 新值:7
指定位置自减后,获取当前值,相当于 --i
位置:0 新值:6
指定位置比较并修改值
位置:0 原始:6
位置:0 修改结果1:true 值:8
位置:0 修改结果2:false 值:8
|
Reference type atomic class
The reference type is used to modify objects, examples of objects are as follows.
1
2
3
4
5
6
7
8
9
|
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Person {
private String name;
private Integer age;
}
|
AtomicReference
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class AtomicReferenceTest {
@Test
void test() {
// 无参构造:默认null
// AtomicReference<Person> atomic = new AtomicReference<>();
// 有参构造:传入初始对象
AtomicReference<Person> atomic = new AtomicReference<>(new Person("张三", 25));
System.out.println("获取当前值:" + atomic.get());
System.out.println("\n设置值");
atomic.set(new Person("李四", 30));
System.out.println("新值:" + atomic.get());
System.out.println("\n获取当前值,并设置新值");
System.out.println("原始:" + atomic.getAndSet(new Person("王五", 35)));
System.out.println("新值:" + atomic.get());
System.out.println("\n比较并修改值");
Person oldP = atomic.get();
Person newP = new Person("赵六", 40);
System.out.println("原始:" + oldP);
System.out.println("修改结果1:" + atomic.compareAndSet(oldP, newP) + "\t值:" + atomic.get());
System.out.println("修改结果2:" + atomic.compareAndSet(oldP, newP) + "\t值:" + atomic.get());
}
}
|
Output.
1
2
3
4
5
6
7
8
9
10
|
获取当前值:Person(name=张三, age=25)
设置值
新值:Person(name=李四, age=30)
获取当前值,并设置新值
原始:Person(name=李四, age=30)
新值:Person(name=王五, age=35)
比较并修改值
原始:Person(name=王五, age=35)
修改结果1:true 值:Person(name=赵六, age=40)
修改结果2:false 值:Person(name=赵六, age=40)
|
AtomicMarkableReference
Code
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
|
public class AtomicMarkableReferenceTest {
@Test
void test() {
// 有参构造:初始化对象和标记
AtomicMarkableReference<Person> atomic = new AtomicMarkableReference<>(new Person("张三", 18), false);
System.out.println("取值:");
System.out.println("对象:" + atomic.getReference());
System.out.println("标记:" + atomic.isMarked());
System.out.println("\n获取当前值并同时将标记存储在boolean类型的数组中(数组长度至少为1)");
boolean[] arr = new boolean[1];
System.out.println("对象:" + atomic.get(arr));
System.out.println("标记:" + arr[0]);
System.out.println("\n设置值和标记");
Person person = new Person("李四", 30);
atomic.set(person, true);
System.out.println("新对象:" + atomic.getReference());
System.out.println("新标记:" + atomic.isMarked());
System.out.println("\n当对象相同时,单独修改标记");
System.out.println("原标记:" + atomic.isMarked());
System.out.println("修改结果:" + atomic.attemptMark(person, false));
System.out.println("新标记:" + atomic.isMarked());
System.out.println("\n比较并修改值");
Person oldP = atomic.getReference();
boolean oldF = atomic.isMarked();
Person newP = new Person("王五", 45);
boolean newF = false;
System.out.println("修改结果1:" + atomic.compareAndSet(oldP, newP, oldF, newF));
System.out.println("对象:" + atomic.getReference());
System.out.println("标记:" + atomic.isMarked());
System.out.println("修改结果2:" + atomic.compareAndSet(oldP, newP, oldF, newF));
System.out.println("对象:" + atomic.getReference());
System.out.println("标记:" + atomic.isMarked());
}
}
|
Output.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
取值:
对象:Person(name=张三, age=18)
标记:false
获取当前值并同时将标记存储在boolean类型的数组中(数组长度至少为1)
对象:Person(name=张三, age=18)
标记:false
设置值和标记
新对象:Person(name=李四, age=30)
新标记:true
当对象相同时,单独修改标记
原标记:true
修改结果:true
新标记:false
比较并修改值
修改结果1:true
对象:Person(name=王五, age=45)
标记:false
修改结果2:false
对象:Person(name=王五, age=45)
标记:false
|
AtomicStampedReference
Code
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
|
public class AtomicStampedReferenceTest {
@Test
void test() {
// 有参构造:初始化对象和标记
AtomicStampedReference<Person> atomic = new AtomicStampedReference<>(new Person("张三", 18), 1);
System.out.println("取值:");
System.out.println("对象:" + atomic.getReference());
System.out.println("版本:" + atomic.getStamp());
System.out.println("\n获取当前值并同时将标记存储在boolean类型的数组中(数组长度至少为1)");
int[] arr = new int[1];
System.out.println("对象:" + atomic.get(arr));
System.out.println("版本:" + arr[0]);
System.out.println("\n设置值和版本");
Person person = new Person("李四", 30);
atomic.set(person, 2);
System.out.println("新对象:" + atomic.getReference());
System.out.println("新版本:" + atomic.getStamp());
System.out.println("\n当对象相同时,单独修改版本");
System.out.println("原版本:" + atomic.getStamp());
System.out.println("修改结果:" + atomic.attemptStamp(person, 3));
System.out.println("新版本:" + atomic.getStamp());
System.out.println("\n比较并修改值");
Person oldP = atomic.getReference();
int oldV = atomic.getStamp();
Person newP = new Person("王五", 45);
int newV = 4;
System.out.println("修改结果1:" + atomic.compareAndSet(oldP, newP, oldV, newV));
System.out.println("对象:" + atomic.getReference());
System.out.println("版本:" + atomic.getStamp());
System.out.println("修改结果2:" + atomic.compareAndSet(oldP, newP, oldV, newV));
System.out.println("对象:" + atomic.getReference());
System.out.println("版本:" + atomic.getStamp());
}
}
|
Output.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
取值:
对象:Person(name=张三, age=18)
版本:1
获取当前值并同时将标记存储在boolean类型的数组中(数组长度至少为1)
对象:Person(name=张三, age=18)
版本:1
设置值和版本
新对象:Person(name=李四, age=30)
新版本:2
当对象相同时,单独修改版本
原版本:2
修改结果:true
新版本:3
比较并修改值
修改结果1:true
对象:Person(name=王五, age=45)
版本:4
修改结果2:false
对象:Person(name=王五, age=45)
版本:4
|
Property Modifiers for Objects
If you need to update a field in a class atomically, you need to use the object’s property modifier atomic class.
Code
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
|
public class AtomicFieldUpdaterTest {
@Test
void test() {
// 整形字段的更新器
// 使用静态方法创建更新器,第一个参数为对象类型,第二个参数为要更新的字段名称,该字段必须修饰为 public volatile
AtomicIntegerFieldUpdater<User1> integerUpdater = AtomicIntegerFieldUpdater.newUpdater(User1.class, "age");
User1 user1 = new User1("Java", 22);
// 获取当前对象被管理字段的值并原子自增
System.out.println(integerUpdater.getAndIncrement(user1));
// 获取当前对象被管理字段的值
System.out.println(integerUpdater.get(user1));
// 引用类型字段的更新器
// 使用静态方法创建更新器,第一个参数为对象类型,第二个参数为要更新的字段类型,第三个参数为要更新的字段名称,该字段必须修饰为 public volatile
AtomicReferenceFieldUpdater<User2, Integer> referenceUpdater = AtomicReferenceFieldUpdater.newUpdater(User2.class, Integer.class, "age");
User2 user2 = new User2("Jerry", 18);
// 获取当前对象被管理字段的值并设置新值
System.out.println(referenceUpdater.getAndSet(user2, 20));
// 获取当前对象被管理字段的值
System.out.println(referenceUpdater.get(user2));
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
class User1 {
private String name;
public volatile int age;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
class User2 {
private String name;
public volatile Integer age;
}
|
Output.
The underlying principles
AtomicInteger
part of the underlying source code.
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
|
public class AtomicInteger extends Number implements java.io.Serializable {
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
}
|
Unsafe
: is the core class of CAS
. Since the Java
methods do not have direct access to the underlying system and need to be accessed through the native (native)
methods, Unsafe
is equivalent to a backdoor, based on which
class allows direct manipulation of memory-specific data. The Unsafe
class is present in the sun.misc
package and its internal method operations can manipulate memory directly as if it were a pointer to C
, since the execution of CAS
operations in Java
depends on the methods of the Unsafe
class. Note that all the methods in the Unsafe
class are native
-modified. This means that the methods in the Unsaf
class call directly on the underlying OS resources to perform the corresponding task.
valueOffset
: indicates the offset address of the variable’s value in memory, since Unsafe
retrieves data based on the memory offset address.
value
: The variable is modified with volatile
to ensure memory visibility between multiple threads.
getAndIncrement()
: AtomicInteger.getAndIncrement()
passes in three values to Unsafe.getAndAddInt()
this
: the current object
valueOffset
: the memory address of the current object
1
: the value to be added
Unsafe.getAndAddInt(Object var1, long var2, int var4)
Source Code
1
2
3
4
5
6
7
|
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
|
- first
this.getIntVolatile(var1, var2)
gets the snapshot value var5
of the current object var1
at memory address var2
- Assuming that some time has passed since the snapshot value was obtained
- then
this.compareAndSwapInt(var1, var2, var5, var5 + var4)
gets the real value of the current object var1
at memory address var2
and compares it to the snapshot value var5
- same: means that the true value of the object has not changed during this time, then update to the desired value
var5 + var4
and return true
, reverse to false
and jump out of the current loop, finally return the original value var5
- different: means that the true value of the object has changed in the meantime, then return
false
, reverse to true
and enter the loop again to retrieve the value and compare it again until it finishes
Another example: suppose two threads, thread A and thread B, perform the getAndAddInt
operation at the same time
- the original value of
value
inside AtomicInteger
is 3, i.e. the value
of AtomicInteger
in main memory is 3. According to the JMM model, thread A and thread B each hold a copy of value
with value 3 in their respective working memory.
- Thread A gets the
value
value 3 via getIntVolatile(var1, var2)
, at which point thread A is hung.
- Thread B gets the
value
value 3 via getIntVolatile(var1, var2)
, just as Thread B is not hung and executes the compareAndSwapInt
method to compare memory values to 3 as well, successfully modifying the memory value to 4.
- At this point Thread A resumes, executes the
compareAndSwapInt
method and finds that its value of 3 does not match the value of 4 in main memory, which means that the value has already been modified by other threads, so Thread A fails this modification and has to read it again and start over.
- Thread A retrieves the
value
value, which is always visible to thread A because the variable value
is modified by volatile
, and thread A continues to execute compareAndSwapInt
to compare and replace it until it succeeds.
CAS
Principle
The full name of CAS
is Compare-And-Swap
, and it is a CPU
concurrent language. Its function is to determine whether the value at a location in memory is the expected value, and if so, to change it to the new value, which is done atomically.
The CAS
concurrency primitive is represented in the JAVA
language by methods in the sun.misc.Unsafe
class. By calling the CAS
method in the UnSafe
class, the JVM
will implement the CAS
assembly instruction for us. This is a completely hardware-dependent function through which atomic operations are implemented. Again, since CAS
is a system primitive, which is an operating system terminology, it consists of a number of instructions that perform a certain function, and the execution of the primitive must be continuous and not interrupted during the execution, i.e. CAS
is a CPU
atomic instruction and does not cause the so-called data inconsistency problem.
The compareAndSwapInt
in the Unsafe
class is a native method, the implementation of which is located in unsafe.cpp
. The following is part of the source code and is available at: http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/tip/src/share/vm/prims/unsafe.cpp
1
2
3
4
5
6
|
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
|
First get the address of the variable value
in memory. This is then compared and replaced by Atomic::cmpxchg
, where the argument x
is the value to be updated and the argument e
is the original memory value.
Disadvantages
- long loop time overhead: there is a
do while
in the source code, and if CAS
keeps failing, it will impose a large overhead on CPU
- only one shared variable can be guaranteed atomic operation: when operating on multiple shared variables, atomicity is not guaranteed
- ABA problems can occur
Problem solving for ABA
Generation of the ABA problem
An important prerequisite for the implementation of the CAS
algorithm is the need to take data out of memory at one point in time and compare and replace it at the current point in time, then at this time difference class will result in a change in the data. Let’s say thread A, thread B takes V1 out of memory, then thread B does something to change the value to V2 and then does something to change the value back to V1, at which point thread A does a CAS operation and finds that V1 is still in memory, and then thread A succeeds. Although Thread A’s CAS operation succeeds, it does not mean that the process is problem-free.
Solution to the ABA problem
Simply use the atomic class AtomicStampedReference
with version control
Code
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
|
public class ABATest {
/**
* 普通的对象引用原子类,不能解决ABA问题
*/
@Test
void atomicReference() {
AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
new Thread(() -> {
atomicReference.compareAndSet(100, 101);
atomicReference.compareAndSet(101, 100);
}, "t1").start();
new Thread(() -> {
// 暂停一会t2线程,保证上面的t1线程完成了一次ABA操作
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100, 2019));
}, "t2").start();
// 等待线程结束
while (Thread.activeCount() > 2) {
// 让出当前线程,给其他线程执行
Thread.yield();
}
System.out.println(atomicReference.get());
}
/**
* 带版本控制的对象引用原子类,可以解决ABA的问题
*/
@Test
void atomicStampedReference() {
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
new Thread(() -> {
// 获取初始版本
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t第1次版本号:" + stamp);
// 暂停一会t1线程,保证t2线程获取到了初始版本号
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100, 101, stamp, ++stamp);
System.out.println(Thread.currentThread().getName() + "\t第2次版本号:" + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101, 100, stamp, ++stamp);
System.out.println(Thread.currentThread().getName() + "\t第3次版本号:" + atomicStampedReference.getStamp());
}, "t1").start();
new Thread(() -> {
// 获取初始版本
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t第1次版本号:" + stamp);
// 暂停一会t2线程,保证上面的t3线程完成了一次ABA操作
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, ++stamp);
System.out.println(Thread.currentThread().getName() + "\t修改成功否:" + result);
}, "t2").start();
// 等待线程结束
while (Thread.activeCount() > 2) {
// 让出当前线程,给其他线程执行
Thread.yield();
}
System.out.println("当前最新实际版本号:" + atomicStampedReference.getStamp() + "\t当前实际最新值:" + atomicStampedReference.getReference());
}
}
|
Outout.
The atomic class AtomicReference
without version control has an ABA
problem.
The atomic class AtomicStampedReference
, which carries version control, can solve the ABA
problem.
1
2
3
4
5
6
|
t1 第1次版本号:1
t2 第1次版本号:1
t1 第2次版本号:2
t1 第3次版本号:3
t2 修改成功否:false
当前最新实际版本号:3 当前实际最新值:100
|