AtomicInteger
AtomicInteger can ensure correct increase and decrease in multithreaded environment
The bottom layer is implemented by cas, volatile and unsafe
Important attribute
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { // Get the offset of the specified attribute through unsafe valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } // Declaring value as volatile guarantees visibility, but not atomicity // That is to say, if a thread successfully modifies the value of variable modified by volatile, it will invalidate the copy of the variable in the working memory of other threads, so that other threads will read the latest value from memory when reading the variable value again // If we do not use the volatile decoration, then one thread modifies the value of the variable, only the value in the working memory, not the value in the main memory, so other variables read the old value in the working memory private volatile int value;
Constructor
Constructor is very simple, that is, simple assignment
public AtomicInteger(int initialValue) { value = initialValue; }
Modify operation
public final int addAndGet(int delta) { // this represents the address of the current atomicinter, and valuesofffset represents the offset of the value attribute // Update with cas return unsafe.getAndAddInt(this, valueOffset, delta) + delta; } 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; }
AtomicStampedReference
AtomicStampedReference is mainly used to solve ABA problems
The so-called ABA problem refers to that when thread A performs cas operation, it first reads the variable value as 0, and then runs out of time slices. Thread B successfully changes the variable from 0 to 1 through cas, and then changes the variable from 1 to 0 through cas. Thread B runs out of time slices, and thread A obtains time slices. At this time, it reads the variable value as 0 again, so thread A judges that the variable has not changed, that is, it has not changed The thread operates on the variable, and then thread A modifies the value of the variable
The solution is to use another version number. When determining whether a variable is modified, we need to determine not only whether the value of the variable has changed, but also whether the version number has changed. Only if both of them have not changed, can our task variables not be changed by other threads
attribute
The most important attribute at the bottom of AtomicStampedReference is a Pair class object
private static class Pair<T> { // Referenced objects final T reference; // Version number final int stamp; private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static <T> Pair<T> of(T reference, int stamp) { return new Pair<T>(reference, stamp); } } private volatile Pair<V> pair;
modify
public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; // The current reference is the same as the expected reference, and the version number is the same // No other thread to modify variables // Next try set // If the new reference is the same as the existing reference and the new version number is the same as the existing version number, then there is no need to set again // If not, try to use cas to modify the variable value in memory return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); } ```java private boolean casPair(Pair<V> cmp, Pair<V> val) { return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); }