AtomicLong is provided by JDK5 to perform atomic operations on long integers. There are similar atomic classes: AtomicBoolean, AtomicInteger, etc., their implementation principle is the same.

Before atoms, the only way to make a long increment atomic was to add synchronized or an explicit lock, a solution that was not only more complicated to write, but also less efficient.

The emergence of atomic classes provides another solution to ensure the atomicity of operations, that is: CAS, for a detailed description of CAS can be seen in another article of the author: “A Little Understanding and Thinking about CAS”.

Using volatile keyword to ensure the order and visibility of the variable, and then using CAS to ensure the atomicity of the operation, can finally ensure the concurrent security of the data.

UML The structure of AtomicLong is very simple, implementedjava.io.SerializableInterface, which means it can be serialized, inheritedjava.lang.Number, which means it is a numeric value and can be converted to other numeric types, such as int, float.

attribute

AtomicLong doesn’t have many attributes. It relies on the compareAndSwapLong() method of the Unsafe class. Only Unsafe can call the underlying CAS operation. It records whether or not the underlying JVM supports lockless updating of the long type. The double and long types are special, occupying 64 bits of space, and can be documented in a separate article later. AtomicLong uses value to represent its specific value, which is volatile to ensure its order and visibility.

/ / need to rely on Unsafe.com pareAndSwapLong atoms () to update the value
private static final Unsafe unsafe = Unsafe.getUnsafe();
// The value attribute is the memory address offset relative to AtomicLong, which is required for CAS operations
private static final long valueOffset;

/* Record whether the underlying JVM supports lockless updates to long types. Because long is a little different from other values, it takes up 8 bytes, takes up two 32-bit space, there is a problem of writing high and low bits. * /
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

/* Record whether the underlying JVM supports lockless updates to the long type and cache it in VM_SUPPORTS_LONG_CAS. * /
private static native boolean VMSupportsCS8(a);

// The result value, which volatile guarantees to be ordered and visible.
private volatile long value;
Copy the code

The constructor

AtomicLong provides two constructors. The default value is 0, or you can manually specify an initial value.

// Specify an initial value manually
public AtomicLong(long initialValue) {
	value = initialValue;
}

// Use the default value 0 for long
public AtomicLong(a) {}Copy the code

Core operations

add

AtomicLong provides two add methods: addAndGet and getAndAdd.

AddAndGet () adds before getting:

/* Atomically adds the given value and returns */
public final long addAndGet(long delta) {
	return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
}
Copy the code

GetAndAdd () adds before getting:

/* Atomically append the given value, returning the result before the operation */
public final long getAndAdd(long delta) {
	return unsafe.getAndAddLong(this, valueOffset, delta);
}
Copy the code

The purpose of both methods is to add value to a given value, which is nothing more than a return value before the operation and a return value after the operation.

Both of them call the unsafe.getAndAddLong() method, and that’s the core:

/* Atomically, add the given value and return the old value. Var1: object instance VAR2 :value The memory address offset relative to the class. Var4: plus a given value */
public final long getAndAddLong(Object var1, long var2, long var4) {
    long var6;
    do {
    	// Read the latest value from main memory
        var6 = this.getLongVolatile(var1, var2);
        // Change the value from VAR6 to (var6 + VAR4) in CAS mode. If the value fails, retry in a loop until it succeeds.
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
	// Return the old value
    return var6;
}
Copy the code

The implementation idea is: read the latest value of the variable from the main memory, and try to modify it through CAS. If the modification fails, it indicates that the variable has been changed by other threads, and the current thread will retry in a loop until it succeeds.

increment

Increment operations, just like add, except add has a value of 1. There are also two methods: getAndIncrement and incrementAndGet, which both increment values, one returning the old value and one returning the new value.

/* Incrementing value atomically, returning the old value. * /
public final long getAndIncrement(a) {
	// Like add(), increment is +1
	return unsafe.getAndAddLong(this, valueOffset, 1L);
}

/* Incrementing value atomically, returning a new value. * /
public final long incrementAndGet(a) {
	return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
Copy the code

decrement

Decrement, again, is the same as add, except add has a value of -1. There are also two methods: getAndDecrement and decrementAndGet, both of which decremented values, one returning the old value and one returning the new value.

/** atomically decrementing the value, returning the old value. * /
public final long getAndDecrement(a) {
	// As with add(), the increment is -1
	return unsafe.getAndAddLong(this, valueOffset, -1L);
}

/** atomically decrementing the value, returning the new value. * /
public final long decrementAndGet(a) {
	return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
}
Copy the code

compareAndSet

Compare and set, atomically, to change the current value from Expect to update, returning true on success and false on failure, just like CAS.

/** Atomically change the current value from Expect to update expect: the old value expected update: the new value to be modified */
public final boolean compareAndSet(long expect, long update) {
	return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
Copy the code

Still rely on the unsafe, unsafe.com pareAndSwapLong () is to call at the bottom of the CAS operation, the native method, using C, you need to call the system function.

weakCompareAndSet

This method is rarely used, and in JDK8, it is identical to the compareAndSet code. It is not implemented until JDK9. What it does is to preserve only the properties of volatile, removing the memory semantics of happens-before rules that guarantee the order and visibility of other variables that are not modified by volatile.

/**
 * Atomically sets the value to the given updated value
 * if the current value {@code ==} the expected value.
 *
 * <p><a href="package-summary.html#weakCompareAndSet">May fail
 * spuriously and does not provide ordering guarantees</a>, so is
 * only rarely an appropriate alternative to {@code compareAndSet}.
 *
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful
 */
// in JDK8, it is the same as compareAndSet
public final boolean weakCompareAndSet(long expect, long update) {
	return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
Copy the code

conclusion

Atomic code is not complex, the logic is very simple, that is, through volatile+CAS to ensure the concurrent security of data. Even though the value is updated almost exclusively by the Unsafe class, the CAS operation itself cannot be implemented in Java code. Instead, it calls the local method and calls the system function through C. The CAS itself relies on the concurrency primitives supported by modern cpus, which ensure that the process of comparing and exchanging itself is not interrupted.