AtomicLong
AtomicLong is an atomic operation class that provides atomic operations on Longs.
An AtomicLong is an Unsafe atomic method for preserving the atomicity of changes. In a multithreaded environment, changing the value of a LONG is performed successfully in only one thread, reducing concurrency.
LongAdder is provided by JDK8 and compensates for AtomicLong’s low concurrency. AtomicLong is a competing update operation on a variable, and only one thread succeeds. If you split a variable into multiple parts and multiple threads operate on multiple variables, this provides a great deal of concurrency, which is how LongAdder is designed. Cells is an array that divides a variable into parts. When returning the value of the variable, the cells array’s value is added to the base value. When modifying a variable, multiple threads compete for the value of the Cell variable in multiple cells, providing concurrency. The Cell uses the @content annotation to indicate that the object will occupy a buffer line. This avoids the problem of pseudo-sharing multiple variables in a buffer line. Volatile Modifies the value from main memory when the thread fetches a value, modifies the value and flusher it to main memory. UNSAFE guarantees atomicity of modification operations.
Striped
LongAdder inherits Striped64, and there are several important variables in Striped64.
Cells are arrays of cells that divide variables into fractions, and multiple threads manipulate multiple variables using CAS.
The values in the Cell are volatile so that other threads can obtain the latest values when they are modified. @contended can be used on TYPE,FIELD, and cells arrays with a single cache line for each cell object. This avoids having multiple variables in a cache line that can only be operated by one thread. Pseudo sharing is avoided.
Take a look at the main method longAccumulate
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
int h;
if((h = getProbe ()) = = 0) {/ / thread prode, initialized to 0 prode ThreadLocalRandom. The current (); // force initialization h = getProbe(); // The specific Cell wasUncontended = assigned to the cells array according to prodetrue; } //TODO: What does this field mean? boolean collide =false; // True if last slot nonempty
for(;;) {// Spin retry Cell[] as; Cell a; int n; long v;if((as = cells) ! = null && (n = as.length) > 0) {//cells not nullif((a = as/(n - 1) & h) = = null) {/ / distribution of judgment whether the cell is emptyif(cellsBusy == 0) {0 lock idle Cell r = new Cell(x); // Optimistically createif(cellsBusy == 0 && casCellsBusy()) {// Lock success Boolean created =false;
try { // Recheck under lock
Cell[] rs; int m, j;
if((rs = cells) ! = null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) { rs[j] = r; created =true; } } finally { cellsBusy = 0; // release the lock}if (created)
break;
continue; // Slot is now non-empty
}
}
collide = false;
}
else if(! WasUncontended) // Allocated cells have values, update wasUncentended wasUncontended =true; // Continue after rehash// Try to modify the value of aelse if (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // The number of threads in the NCPU systemelse if(n >= NCPU || cells ! = as) collide =false; // At max size or stale
else if(! collide) collide =true; Cells and as refer to the new array and the old array respectivelyelse if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == as) { // Expand table unless stale
Cell[] rs = new Cell[n << 1];
for (int i = 0; i < n; ++i)
rs[i] = as[i];
cells = rs;
}
} finally {
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table } h = advanceProbe(h); } // If the cells array is empty, initialize and assign the lockelse if (cellsBusy == 0 && cells == as && casCellsBusy()) {
boolean init = false;
try { // Initialize table
if (cells == as) {
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break; } // Finally, try to modify cellsBusy failed, try to modify base, modify successfully out of the loopelse if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}
Copy the code
DoubleAccumulate is similar to longAccumulate.
LongAdder
Let’s look at the Add method
public void add(long x) { Cell[] as; long b, v; int m; Cell a; If the array is not empty, execute the following code, otherwise try to load the value to base, if successful, save, otherwise execute the following codeif((as = cells) ! = null || ! casBase(b = base, b + x)) { boolean uncontended =true; // Execute Striped64's longAccumulate method if the array is empty; // otherwise execute longAccumulate if the cell assigned by the thread's prode is empty // Otherwise, use cas to modify the assigned cell. If this fails, execute longAccumulateif (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate(x, null, uncontended);
}
}
Copy the code
And then look at the sum method
public long sum() { Cell[] as = cells; Cell a; Volatile long sum = base; volatile long sum = base;if(as ! = null) {for (int i = 0; i < as.length; ++i) {
if((a = as[i]) ! = null) // Iterate over cells, add the values of all cells and return base. // Cells is also volatile. If a cell is modified by another thread, the thread calling sum takes the latest value of the cells sum += a.value; }}return sum;
}
Copy the code