Atomic class: A good example of a lockless tool class

The advantage over mutex is performance optimization, taking advantage of the HARDWARE support of the CPU, which provides CAS instructions (Compare And Swap). The CAS instruction contains three parameters: A, the memory address of the shared variable, B, the value for comparison, and C, the new value of the shared variable. Only when the value of memory address A is equal to that of memory address B can the value of memory address A be changed to C. As a CPU instruction.

CAS relevant

As shown in the following source code interpretation, this section is mainly the method of CAS-related operations.

/**
	*  CAS
  * @paramO contains the object to modify the field *@paramOffset Specifies the offset * of a field in the object@paramExpected value *@paramUpdate Updates the value *@return          true | false
  */
public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object update);

public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
  
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);
Copy the code

What is a CAS? Compare and replace, a technique that is often used to implement concurrent algorithms. The CAS operation contains three operands — memory location, expected original value, and new value. When the CAS operation is performed, the value of the memory location is compared to the expected value. If it matches, the processor automatically updates the value of the memory location to the new value; otherwise, the processor does nothing. Addressing Unsafe, CAS is an atomic CPU instruction (CMPXCHG) that doesn’t cause data inconsistencies. Its underlying implementation of CAS methods such as compareAndSwapXXX is the CPU instruction CMPXCHG.

Typical applications

CAS in Java. Util. Concurrent. Atomic related classes, Java AQS, CurrentHashMap implementation has a very wide application. As shown in the figure below, in the implementation of AtomicInteger, the static field valueOffset is the memory offset address of the field value. The value of valueOffset when AtomicInteger is initialized, Retrieved from the Unsafe objectFieldOffset method in static code blocks. In the thread-safe method provided in AtomicInteger, the value of the field valueOffset can be used to locate the memory address of the value in the AtomicInteger object so that atomic operations on the value field can be implemented according to CAS.

The following figure shows the memory diagram of an AtomicInteger object before and after the increment operation. The baseAddress of the object is “baseAddress= 0x110000”. Run the baseAddress+valueOffset command to obtain the memory address of the value valueAddress= 0x11000C. The atomic update operation is then performed through CAS, and returns if the update is successful, otherwise retry until the update is successful.

This paragraph is quoted from meituan technical team blog

ABA problem

It is possible that thread T1, after executing code ① and before executing code ②, will update count to B by thread T2 and then back to A by thread T3, so that thread T1 will always see A, but in fact it has been updated by another thread. This is an ABA problem.

We may not care about ABA in most cases, such as atomic increments, but we may not care about ABA in all cases. For example, an atomized update object may need to care about ABA because the two A’s are equal, but the properties of the second A may have changed. Therefore, it is important to check the CAS scheme before using it.

An overview of atomic classes

  • Basic data types: atomized I ++, I –,++ I,– I, can also be evaluated by passing in functions;
  • Object reference type: Resolve ABA join tag

  • Array: Atomically updates each element in the array. The only difference between the methods provided by these classes and the basic atomized data types is that each method has an additional array of index parameters;
  • Object property updaters: They can be used to update the properties of objects atomically. All three methods are implemented by reflection mechanism. Note that the object attributes must be volatile to ensure visibility; If the object attribute is not volatile, the newUpdater() method throws the runtime exception IllegalArgumentException.
  • Accumulator: Faster than atomized primitive data types, but does not support the compareAndSet() method. If you only need to accumulate operations, it is better to use atomized accumulators.