I'm analyzing the LongAdder
algorithm in detail. LongAdder
extends the class Striped64
and in that class the essential method is retryUpdate
. The following piece of code is taken from this method; in the linked source code it occupies lines 212–222:
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 {
busy = 0;
}
Question: How can this try
block fail?
Note that the array access
rs[j = (m - 1) & h]
shouldn't throw an IndexOutOfBoundsException
because the result of a bitwise-and operation is always less than or equal than the minimum of its integer arguments, hence 0 <= j <= m-1 is within the bounds of the array.
This very much looks like the pattern used with ReentrantLock
everywhere else in the jdk code itself. The "pattern" here is that you should always release the lock, even if an Exception occurred, so usually the code is written as:
Lock someLock...
try {
// use someLock
} finally {
someLock.unlock();
}
Since cellsBusy
(it was renamed from busy
) is actually a busy-spin lock, the pattern is sort of the same here. As such:
cellsBusy = 0;
is actually "releasing the lock". So this is not really about failing, as it is about releasing the lock explicitly. I find this a lot easier to read and reason about the code.
This code - and literally any other Java code before version 11 - could fail as a result of the deprecated Thread.stop
method being called from another thread. This results in a ThreadDeath
error being thrown in the targeted thread, potentially at any time. However, the thread does at least stay alive long enough for the finally
block to execute.
The Thread.stop
method is deprecated because this behaviour makes it "inherently unsafe":
Why is Thread.stop deprecated?
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.
In theory the code could have been written this way as an attempted defense against leaving the object in an invalid state if the thread it is executed in is stopped from another thread. That said, it is very difficult to guarantee a valid state if Thread.stop
could be called at any time, and not very common to even attempt to do so, so it's not likely that this was the author's intention. (If it was, the code would probably have a comment explaining it.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With