This is the 8th day of my participation in the August Text Challenge.More challenges in August

AtomicReference

Under the Java. Util. Concurrent. Atomic package AtomicInteger class to packaging, the integer that is similar to i++ operation can be changed into atomic operation, so for general object types to how to realize atomic operations, starting from Java 1.5, The JDK provides the AtomicReference class to ensure atomicity between reference objects.

public class AtomicReferenceDemo {
    static AtomicReference<Student> stu = new AtomicReference<>();

    public static void main(String[] args) {
        Student lemon = new Student("lemon".17);
        stu.set(lemon);
        Student blue = new Student("blue".19);
        boolean res = stu.compareAndSet(lemon, blue);
        System.out.println(res + "\tstu="+ stu.get()); }}Copy the code

Running results:

true stu=Student{name=’blue’, age=19}

But as with AtomicInteger, ABA problems still occur with CAS

Code that produces ABA problems

public class ABADemo {
    public static void main(String[] args) {
        //ABA problem code
        AtomicReference<Integer> atomicReference = new AtomicReference<>();
        atomicReference.set(100); // The original value is 100
        new Thread(() -> {
            boolean res1 = atomicReference.compareAndSet(100.101);
            boolean res2 = atomicReference.compareAndSet(101.100);
            System.out.println("res1=" + res1);
            System.out.println("res2=" + res2);
        }).start();
        new Thread(() -> {
            boolean res3 = atomicReference.compareAndSet(100.200);
            System.out.println("res3="+ res3); }).start(); }}Copy the code

Res1, RES2,res3 are all true, which indicates that although thread T1 has modified the value in atomicReference, the value after modification is the same as the original value, so thread T2 considers that the CAS operation has not been modified successfully.

AtomicStampedReference solves ABA problems

In order to solve the problem of ABA, introduces AtomicStampedReference, AtomicStampedReference inside it not only maintain the object values, also maintains a timestamp (version number). If the value of an AtomicStampedReference is modified, you must update the timestamp in addition to the data.

Constructor for AtomicStampedReference

Two parameters are passed during initialization, the initialized reference value and the version number

public AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}
Copy the code

CompareAndSet method of AtomicStampedReference

public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    Pair<V> current = pair;
    return
        expectedReference == current.reference &&
        expectedStamp == current.stamp &&
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
         casPair(current, Pair.of(newReference, newStamp)));
}
Copy the code
  • ExpectedReference: Expect value
  • NewReference: The new value to be updated to
  • ExpectedStamp: Indicates the expected version number
  • NewStamp: indicates the version number to be updated after the CAS operation succeeds

Then, each operation will compare the version number first. The operation succeeds only when the version number is the same. After each operation succeeds, the version number will be increased by +1 (the version number is only added but not decreased).

The test code

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class ABASolve {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100.1);
        int stamp = atomicStampedReference.getStamp();
        new Thread(() -> {
            System.out.println(T1 thread gets the initial version number: + stamp);
            System.out.println(T1 thread gets the initial value: + atomicStampedReference.getReference());
            //
            boolean res1 = atomicStampedReference.compareAndSet(100.101, stamp, atomicStampedReference.getStamp() + 1);
            System.out.println("Modified result :" + res1);
            System.out.println(T1 thread modified version number: + atomicStampedReference.getStamp());
            System.out.println(T1 thread modified value: + atomicStampedReference.getReference());
            boolean res2 = atomicStampedReference.compareAndSet(101.100.2, atomicStampedReference.getStamp() + 1);
            System.out.println("Modified result :" + res2);

        }).start();
        new Thread(() -> {
            System.out.println(T2 thread gets initial version number: + stamp);
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            boolean b1 = atomicStampedReference.compareAndSet(100.101, stamp, atomicStampedReference.getStamp() + 1);
            System.out.println("The version number expected by t2 thread is + stamp + ", the actual version number of t2 thread atomicStampedReference is: + atomicStampedReference.getStamp());
            System.out.println("The result of the CAS operation on t2 thread is :"+ b1); }).start(); }}Copy the code

This code simulates that thread 1 changes the atomicStampedReference value and then changes it back to the original value, and observes whether the version number changes and the CAS operation of thread 2 is successful. The result is as follows:

T1 thread gets the initial version number:1T1 thread gets the initial value:100Modification result:trueVersion number of t1 thread after modification:2T1 thread modified value:101Modification result:trueT2 thread gets the initial version number:1The version number expected by the T2 thread is1The actual version number of t2 thread atomicStampedReference is as follows:3The result of CAS operation of T2 thread is:false
Copy the code

conclusion

1.AtomicReference allows object types to be wrapped like integer types to achieve atomic operations

2.AtomicStampedReference adds the stamp (version number) attribute to the AtomicReference. Each successful operation increases the version number to ensure that NO ABA problem occurs during CAS (because the data is modified, the version number must be different).