Master high concurrency, high availability architecture
Lesson 2 Concurrent Programming
Learn concurrent programming from this lesson. This paper mainly introduces the basic knowledge of concurrent programming, lock, memory model, thread pool and the use of various concurrent containers.
Section 7 Concurrent Classification of Atoms
Atomic CAS ABA
The Famous ABA Problem
For example, a full glass of water on the table is knocked over, cleaned and cleaned, then poured a glass, which others think is the same as before. Thread 1 is the party, thread 2 is someone else, and shared variable V is the glass of water. Thread 1 and thread 2 get the initial value A of shared variable V at the same time, and deal with it separately. Thread 1 updates the value to B and A. Thread 2 finds that it is A when it takes value again, and then does its processing. This scenario is incorrect in some cases, such as a shopping mall launching activities, where the residual amount of membership card is less than 100 yuan, the shopping mall will recharge 20 yuan for two days. As a result, because the membership card had been recharged once 20 yuan, he spent 50 yuan, the balance is less than 100 yuan, which will recharge 20 yuan again, resulting in one. Membership cards are recharged many times.
The following code demonstrates an ABA problem
public void aba() { AtomicInteger abaInt = new AtomicInteger(100); Thread t1 = new Thread() { public void run() { abaInt.compareAndSet(100, 101); System.out.println(String.format("thread t1, abaInt: %s", abaInt.get())); abaInt.compareAndSet(101, 100); System.out.println(String.format("thread t1, abaInt: %s", abaInt.get())); } }; Thread t2 = new Thread() { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } boolean success = abaInt.compareAndSet(100, 101); System.out.println(String.format("thread t2, abaInt: %s, isSuccess: %s", abaInt.get(), success)); } }; t1.start(); t2.start(); }
thread t1, abaInt: 101 thread t1, abaInt: 100 thread t2, abaInt: 101, isSuccess: true
The value of 100 compared in thread t2 is actually no longer the previous value of 100. Variables in memory addresses have undergone changes in A-> B-> A.
The root cause of ABA problem is that it is impossible to judge whether the variables have been changed, so the solution is to add state to the shared variables to record whether the shared variables have been modified.
Atomics
Under the java.util.concurrent.atomic package, atomic classes have the following classes:
AtomicBoolean Atoms Update Boolean Values The Value of Atomic Integer Atomic Addition and Decrease Numbers AtomicLong Atoms Update Long Number Type Values AtomicReference Atom Updates Reference Type Value Atomic Integer Array Atom Updates Elements of Digital Array Atomic Long Array Atoms Update Elements of Long Digital Arrays AtomicReference Array Atom Updates Elements of Reference Type Array The value of the numeric type field of the Atomic IntegerField Updater atomic update class The Value of Long Number Type Field of Atomic LongField Updater Atomic Update Class The value of the reference type field of the Atomic Reference Field Updater atomic update class Atomic StampedReference Atom updates the value of reference type with version number to solve ABA problem
The steps of atom updating field:
- Since the classes of atomic update fields are abstract classes, you need to use the static method newUpdater() to create updates and specify the classes and fields to be updated.
AtomicIntegerFieldUpdater<User> upd = AtomicIntegerFieldUpdater.newUpdater(User.class, 'age');
- The field to be updated must be public volatile
The foundation of atomic operation is CAS, which gives the operation to the atomicity of hardware bottom layer.
Here's a code to avoid ABA problems:
public void noAba() { AtomicStampedReference<Integer> noAbaInt = new AtomicStampedReference<Integer>(100, 0); Thread t1 = new Thread() { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } noAbaInt.compareAndSet(100, 101, noAbaInt.getStamp(), noAbaInt.getStamp() + 1); System.out.println(String.format("thread t1, noAbaInt: %s", noAbaInt.getReference())); noAbaInt.compareAndSet(101, 100, noAbaInt.getStamp(), noAbaInt.getStamp() + 1); System.out.println(String.format("thread t1, noAbaInt: %s", noAbaInt.getReference())); } }; Thread t2 = new Thread() { public void run() { int stamp = noAbaInt.getStamp(); System.out.println("before stamp " + stamp); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("after stamp " + noAbaInt.getStamp()); boolean success = noAbaInt.compareAndSet(100, 101, stamp, stamp + 1); System.out.println(String.format("thread t2, noAbaInt: %s, isSuccess: %s", noAbaInt.getReference(), success)); } }; t1.start(); t2.start(); }
before stamp 0 thread t1, noAbaInt: 101 thread t1, noAbaInt: 100 after stamp 2 thread t2, noAbaInt: 100, isSuccess: false
You can see that the thread t2 update failed because the version number is not the expected value, so the update failed.