Wedge
In the previous article, we introduced synchronized and learned that synchronized can solve the atomicity problem of some data. In this article, we continue to learn the knowledge of CAS non locking with AtomicInteger as the starting point.
Using synchronized to solve the atomicity of i + +
Use the synchronized keyword to ensure data atomicity
public class Test { static int synchronizedValue = 0; public static void main(String[] args) { // Using synchronized++ synchronizedAdd(); } private static void synchronizedAdd() { for(int i = 0; i < 50; i++) { new Thread(() -> { synchronized(Test.class) { System.out.println("synchronizedAdd: "+ ++Test.synchronizedValue);; } }).start(); } } }
result:
synchronizedAdd: 50
Solving self increasing atomicity using AtomicInteger
static AtomicInteger atomicValue = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { // Using atomic++ atomicAdd(); }
private static void atomicAdd() { for(int i = 0; i < 50; i++) { new Thread(() -> System.out.println("atomicAdd: "+ Test.atomicValue.incrementAndGet())).start(); } }
result
atomicAdd: 50
CAS principle
What is CAS? The full English name is compare and swap, which means comparison and exchange when translated into Chinese. The professional term is optimistic lock. In other words, when we modify the data, we will try to compare whether this value has been modified by others. If it has not been modified, we can modify it ourselves; If it has been modified, we will retrieve the latest value and repeat the above steps to compare again. Next, we further analyze CAS from AtomicInteger source code.
AtomicInteger source code analysis
Looking at the source code of AtomicInteger, we find that it can be divided into several parts.
- Unsafe: core class, which is really responsible for executing CAS operations
- Value and valueOffset: value and offset
- API interface: various usage modes are provided externally, mainly encapsulating some Unsafe operations
Unsafe
Unsafe, as its name implies, is an unsafe class with a large number of native methods. In principle, JDK does not allow us to use it. It is a class for internal use in JDK. First of all, his constructor is private and cannot be instantiated manually. Second, although he provides unsafe Getunsafe () method to obtain an instance, but it has a judgment. If it is not loaded by the system, it will directly throw an exception. The source code mentioned above is as follows:
// (1) Construction method privatization private Unsafe() { } public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); // (2) If Unsafe is not loaded by the JVM, an error will be reported if (!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } }
valueOffset
When the class is initialized, a static code block will be loaded, and the offset of a final tag will be determined through unsafe. The source code is as follows
private static final long valueOffset; // When the class is initialized, the static code block will be executed to determine the offset of a final tag through unsafe static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
Then we return to the method atomicAdd we wrote ourselves
private static void atomicAdd() { for(int i = 0; i < 50; i++) { new Thread(() -> System.out.println("atomicAdd: "+ Test.atomicValue.incrementAndGet())).start(); } }
Follow up the incrementAndGet method, which is a wrapper method that directly calls unsafe getAndAddInt
public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
Follow up unsafe Getandaddint method, OK, the following is the code we need to analyze
public final int getAndAddInt(Object Current object, long Offset, int Self increment) { int Object value; do { // The purpose of this local method is, // From the AtomicInteger object instance, obtain the position of the value field according to the valueOffset offset offset, so as to obtain the value of the current value Object value/Calculated object value = this.getIntVolatile(Current object, Offset); } // If the [calculated value] is inconsistent with the [current object + offset], the compare will be false this time, and the next cycle will be entered // If the [calculated value] is consistent with the [current object + offset], then compare is true this time. At this time // [value of object] = [value of calculated object] + [self increment], and jump out of the loop while(!this.compareAndSwapInt(Current object, Offset, Calculated object value, Calculated object value + Self increment)); return Object value; }
Some common problems of Atomic CAS
ABA problem
For example, you only operate when A value is A. Therefore, the following situations may occur
- Initially A
- A->B
- B->A
- Start operation
(2) In step (3), the value has been changed, but the value is the same as what I expected. Therefore, when we go to compareAndSwapInt, we will find that the value is still A and the setting is successful.
Note: how to solve ABA problem? Just add a time stamp. Bring a time stamp when comparing.
Infinite cycle problem
Look at the source code, because we use the do... while loop, it may cycle many times, but it is not successful.
Note: how to solve the infinite loop problem? jdk provides us with an idea to segment CAS. Interested readers can refer to the LongAdder class.
Custom object atomic problem
AtomicInteger can only guarantee the atomicity of a variable.
Note: what about complex objects? jdk provides us with AtomicReference, which compares whether the reference of this object is a reference.