I am ABin abin: write a lifetime code and create a world-class story. If my friends think my article is a little feel, then point a praise before you go.
1, CAS introduction
- Full name: Compare and swap, literal meaning: [compare and exchange]
1. What are the operations of CAS
- Requirement scenario: we assume the original data V in memory, the old expected value A, and the new value B to be modified.
- Three steps:
- Compare whether A and V are equal. (comparison)
- If the comparisons are equal, write B to V. (exchange)
- Returns whether the operation was successful.
2. Precautions:
- When multiple threads perform CAS operations on a resource at the same time, only one thread can operate successfully, but it will not block other threads, and other threads will only receive the signal of operation failure. It can be seen that CAS is actually an optimistic lock.
3. Code demonstration:
/** * @description: Compare and swap Abbreviation: CAS Chinese: compare and exchange */ public class CASDemo { public static void main(String[] args) { //Atomic class is mainly used for efficient program processing in high concurrency environment to help us simplify synchronization AtomicInteger atomicInteger = new AtomicInteger(2021); //Compare and set parameter 1: expected value parameter 2: update value boolean update = atomicInteger.compareAndSet(2021, 2022); if (update) { System.out.println("Modified successfully:" + atomicInteger.get()); } else { System.out.println("Modification failed!:" + atomicInteger.get()); } //Increments the current value by + 1 atomically atomicInteger.getAndIncrement(); boolean flag = atomicInteger.compareAndSet(2021, 2022); if (flag) { System.out.println("Modified successfully:" + atomicInteger.get()); } else { System.out.println("Modification failed!:" + atomicInteger.get()); } } }
Operation results:
Introduction to AtomicInteger class:
- java. util. concurrent. The atomic class under atomic package is mainly used for efficient program processing in high concurrency environment to help us simplify synchronization.
- Not only this class, but also AtomicBoolean, AtomicInteger, AtomicLong, AtomicLongArray, AtomicReference, etc.
AtomicInteger source code analysis:
- Little friends who don't understand Volatile and Unsafe can read my article: Specific introduction and use of JMM and Volatile
getAndIncrement() method source code analysis:
- From the source code below, we can see that Java is obtained directly from memory, so the efficiency is very high
- At the same time, the white source code below is a locking mechanism: spin lock. Compare the value in the current working memory with the value in the main memory. If this value is expected, then perform the operation. If not, then (do while infinite loop)
4. Disadvantages of CAS
- For performance problems, we use the while true method to modify the data most of the time until it succeeds.
- The advantage is that it is extremely fast, but when the number of threads keeps increasing, the performance decreases significantly, because each thread needs to be executed and occupies CPU time.
- ABA problem
2, ABA problem
1. What is ABA
- For example, Zhang San now inquired that the Chinese score in the student table was 96. When Zhang San wanted to modify this data, he checked again and found that it was still 96. So at this time, from an intuitive point of view, this data has not been modified by others, but has it really not been modified?
- In fact, during the second time of Zhang San's query, this 96 is likely to be changed to 69 by Li Si. As a result, Li Si Yi didn't see it right, changed it wrong, and flustered it to 96.
- Then, CAS will mistakenly think that this value has always been 96 and has never been changed. Is there a problem? All these problems are collectively referred to as: CAS operation - > ABA problem.
2. Code demonstration
/** * @description: CAS: ABA Problem test */ public class ABADemo { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(96); // If my expected value is reached, I will update it. Otherwise, I will not update it System.out.println(atomicInteger.compareAndSet(96,100)); System.out.println(atomicInteger.get()); // ==============Troublemaker Li Si================== System.out.println(atomicInteger.compareAndSet(100,69)); System.out.println(atomicInteger.get()); // ==============Expected results================== System.out.println(atomicInteger.compareAndSet(69,100)); System.out.println(atomicInteger.get()); } }
result:
3. How to avoid ABA problem
- Atomic reference: (optimistic locking idea) AtomicStampedReference class can solve ABA problem. This class maintains a [version number] Stamp, which is actually a bit like an optimistic lock. When performing CAS operations, you should compare not only the current value, but also the version number. The update operation is performed only if both are equal.
be careful:
- AtomicStampedReference<>(6, 1); These two parameters should not be filled in too large, because we use Integer type in this test
- Integer uses the object caching mechanism. The default range is - 128 ~ 127. It is recommended to use the static factory method valueOf to obtain object instances instead of new, because valueOf uses caching, and new will create new objects and allocate new memory space.
Code demonstration:
/** * @description: Solve ABA problems caused by CAS */ public class ABADemo2 { static AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(6, 1); public static void main(String[] args) { //Use the version number mechanism to verify ABA problems new Thread(() -> { //Get current version number int stamp = atomic.getStamp(); System.out.println("thread A1 The version number of is:" + stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomic.compareAndSet(6, 10, atomic.getStamp(), atomic.getStamp() + 1); System.out.println("thread A2 The version number of is:" + atomic.getStamp()); System.out.println(atomic.compareAndSet(10, 6, atomic.getStamp(), atomic.getStamp() + 1)); System.out.println("thread A3 The version number of is:" + atomic.getStamp()); }, "thread A:").start(); //Solve ABA problem with the idea of [optimistic lock] new Thread(() -> { int stamp = atomic.getStamp(); System.out.println("thread B1 The version number of is:" + stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomic.compareAndSet(6, 2, stamp, stamp + 1)); System.out.println("thread B2 The version number of is:" + atomic.getStamp()); }, "thread B: ").start(); } }
Result display: