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    修改结果1true     8
位置0    修改结果2false    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.

1
2
3
4
22
23
18
20

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);
    }
}
  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.
  2. valueOffset: indicates the offset address of the variable’s value in memory, since Unsafe retrieves data based on the memory offset address.
  3. value: The variable is modified with volatile to ensure memory visibility between multiple threads.
  4. getAndIncrement() : AtomicInteger.getAndIncrement() passes in three values to Unsafe.getAndAddInt()
    1. this: the current object
    2. valueOffset : the memory address of the current object
    3. 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;
}
  1. first this.getIntVolatile(var1, var2) gets the snapshot value var5 of the current object var1 at memory address var2
  2. Assuming that some time has passed since the snapshot value was obtained
  3. 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
    1. 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
    2. 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

  1. 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.
  2. Thread A gets the value value 3 via getIntVolatile(var1, var2), at which point thread A is hung.
  3. 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.
  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.
  5. 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

  1. 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
  2. only one shared variable can be guaranteed atomic operation: when operating on multiple shared variables, atomicity is not guaranteed
  3. 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.

1
2
true
2019

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