[concurrency-interest] Atomic non-null then null

Jeff Hain jeffhain at rocketmail.com
Wed Aug 8 17:14:45 EDT 2018


Hello.

Could someone confirm (or invalidate :) that the following class works?

The "tricky part" is that get() is supposed to be callable concurrently.
If it always gets called by a same thread, it should obviously work
since only this thread would deal with the non-volatile reference.



/**
 * When wanting to retrieve an object from an AtomicReference
 * that is initially non-null and then is only set once and with null,
 * this class allows to get rid of the volatile read overhead
 * after the nullification has been detected.
 */
public class AtomicNonNullThenNull<T> {

    private final AtomicReference<T> aRef;

    private Object ref = this;

    /**
     * @param aRef The initially non-null reference must be set into it
     *        early enough for first call to get() method to see it.
     */
    public AtomicNonNullThenNull(AtomicReference<T> aRef) {
        this.aRef = aRef;
    }

    /**
     * Must only be called from threads to which the initial state
     * of this instance has properly been made visible.
     * 
     * @return The reference retrieved from the AtomicReference specified
     *         to the constructor, until a call to this method detects that
     *         it became null, after which null is always returned,
     *         from a non-volatile read.
     */
    public T get() {
        final Object ref = this.ref;
        if (ref == null) {
            return null;
        } else {
            final T vRef = this.aRef.get();
            if (vRef == null) {
                // Possibly done by multiple threads.
                this.ref = null;
                return null;
            } else {
                return vRef;
            }
        }
    }
}



NB1: I use it in a method that gets called a lot,
to do something once if it ever gets called,
as follows:
    final Runnable runnable = anntn.get();
    if ((runnable != null) && aRef.compareAndSet(runnable, null)) {
        runnable.run();
    }
I just read the discussion about "Unsynchronized lazy condition" from June,
and the "Effective Java" class load trick, which seems to solve a similar
problem.
The cons that I see for the class load trick are that you need one class per use case,
that it is always "slow" when it triggers (class load),
and that it can only "fire" once (with ANNTN you could do aRef.set(null) only after a few runs),
and the pro that in the long run the overhead is smaller due to the method being empty
(unless the JIT can see that once the value is null it can never get back to non-null,
and optimize the code away).



NB2: There is a dual "AtomicNullThenNonNull" using a similar idea,
but it only works with objects with final fields or such,
since it can make them visible without proper memory barriers.



-Jeff

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20180808/df50d342/attachment.html>


More information about the Concurrency-interest mailing list