Atomic variables and CAS for concurrent programming in java

We know that the implementation of locks can be divided into optimistic locks and pessimistic locks. For details, please refer to my article on the locking mechanism and principle of the database . There are also corresponding implementations of optimistic locks and pessimistic locks in java. In the previous article, we discussed ReentrantLock and synchronized . They are both specific implementations of pessimistic locks, and they are all guaranteed to be locked before they operate. There is also an implementation of optimistic locking in java, which is the CAS (compareAndSwap) mechanism.

Disadvantages of locks

  • If the lock is already taken, then other threads must be suspended. When a thread resumes execution, it must wait for other threads to finish executing their time slices before being scheduled for execution. There is a lot of overhead in suspending and resuming threads, etc.
  • While a thread is waiting for a lock, it cannot do anything else. If a thread is delayed while holding the lock (such as a page fault, scheduling delay), then all threads that need the lock will not be able to execute. And priority inversion will occur , that is, the thread with higher priority still needs to wait, causing its priority to decrease.

CAS implementation

CAS is an implementation of optimistic locking, which requires a contact conflict check mechanism to determine whether there is interference from other threads during the update process. If present, the operation will fail and can be retried . Specifically, CAS has 3 operands, the memory value V, the old expected value A, and the new value B to be modified. Modify memory value V to B if and only if expected value A and memory value V are the same, otherwise do nothing.

int A;
do {
    A = value.get();
} 
while (V != value.compareAndSwap(A,B));

Problems with CAS

  1. ABA problem.

    ABA is an abnormal phenomenon. The abnormal point is that each time we compare the memory value V and the old expected value A to see if they are equal. If they are equal, we determine that the value of V has not changed before this, but this is not the case. It is quite possible that the value of V undergoes changes in ABA without us being aware of it.

    There is a relatively simple solution to the ABA problem: instead of updating the value of a reference, update two values, including a reference and a version number. Even if the value changes from A to B and then back to A, the version numbers are different. Java neutralization AtomicStampedReferencesupports AtomicMarkableReferenceperforming atomic conditional updates on two variables, enabling version control.

  2. Livelock problem. Cyclic latency is expensive

    The so-called livelock is that the thread has been in the running state but is doing useless work. For example, the while loop is trying to update, but each retry fails, causing other tasks of the thread to not be executed.

    The solution to the livelock problem can introduce a certain amount of randomness into the retry mechanism.

atomic variable

Atomic types such as AtomicInteger in java are implemented using CAS. See the source code.

/**
 * Atomically increments by one the current value.
 *
 * @return the previous value
 */
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

look againunsafe.getAndAddInt

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

Sure enough, the implemented code is the same as our previous template.

Let's look at the performance comparison between atomic variables and general locks. At medium and low levels of competition, atomic variables can provide higher scalability, and under high competition, locks can effectively avoid competition. (Excerpted from "java Concurrent Programming Practice" Page-269)

Scalability means that when computing resources (such as CPU, memory, storage capacity, or IO bandwidth) are increased, the throughput or processing power of a program can increase accordingly.

There's a special caveat here: the atomic scalar class doesn't extend basic wrapper classes such as Integer. The wrapper classes for primitive types are not modifiable, whereas atomic variables are modifiable. There are also no redefinitions hashcodeand equalsmethods in atomic variables, each instance is different, so they also cannot be used as keys in hash-based containers.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325671526&siteId=291194637