Java Concurrent Programming Chapter 15 Notes

Chapter 15 Atomic Variables and Non-Blocking Synchronization Mechanisms

- Compare and Convert (CAS)

CAS contains three operands: the memory location V to be read and written, the value A to be compared, and the new value B to be written. CAS will update the value of V with B by means of origin if and only if the value of V is equal to A, otherwise it does nothing.

Optimistic locking uses this mechanism.

Typical usage patterns of CAS: 

First read the value A from V, calculate the value B from A, and then change the value A of V to B in an atomic operation through CAS.

A mock CAS:

/**
 * Simulate CAS operation
 * @author cream
 *
 */
public class SimulatedCAS{
	private int value;
	
	public synchronized int get(){ return value;}
	
	public synchronized int compareAndSwap(int expectedValue,int newValue){
		int oldValue = value;
		if(oldValue==expectedValue)
			value = newValue;
		return oldValue;
	}
	
	public synchronized boolean compareAndSet(int expectedValue,int newValue){
		return (expectedValue == compareAndSwap(expectedValue, newValue));
	}
}

Thread-safe counter implemented using CAS:

class CASCounter{
	private SimulatedCAS value;
	
	public int getValue(){
		return value.get();
	}
	
	public int increment(){
		int v;
		do{
			v=value.get();
		}
		while(v != value.compareAndSwap(v, v+1));
		return v+1;
	}
}

Incrementing takes the standard form: read the old value, calculate the new value (+1) from it, and use CAS to set the new value. If CAS fails, the operation will be retried immediately.

Disadvantages of CAS: Although CAS is very efficient to solve atomic operations, there are still three major problems with CAS. ABA problem, long loop time and high overhead and only guarantees atomic operations on one shared variable

ABA problem: Because CAS needs to check whether the value has changed when operating the value, and if there is no change, update it, but if a value was originally A, became B, and then became A, then use CAS to check You will find that its value has not changed, but it has actually changed. The solution to the ABA problem is to use the version number. Append the version number in front of the variable, and add one to the version number each time the variable is updated, then A-B-A will become 1A-2B-3A.

two atomic variables

AtomicInteger to study how to achieve data correctness in the absence of locks.

private volatile int value;

First of all, it may be necessary to use the volatile primitive to ensure that the data between threads is visible (shared) without the lock mechanism. Only in this way can you directly read the value of the variable.

public final int get() {
        return value;
}

Then let's see how ++i does it.

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}
The CAS operation is used here, each time the data is read from the memory and then the CAS operation is performed on this data and the result after +1, if successful, the result is returned, otherwise, retry until successful.


Guess you like

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