Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guava: MemoizingSupplier thread safety

Guava Suppliers-class contains MemoizingSupplier:

static class MemoizingSupplier<T> implements Supplier<T>, Serializable {
    final Supplier<T> delegate;
    transient volatile boolean initialized;
    // "value" does not need to be volatile; visibility piggy-backs
    // on volatile read of "initialized".
    transient T value;

    MemoizingSupplier(Supplier<T> delegate) {
      this.delegate = delegate;
    }

    @Override public T get() {
      // A 2-field variant of Double Checked Locking.
      if (!initialized) {
        synchronized (this) {
          if (!initialized) {
            T t = delegate.get();
            value = t;
            initialized = true;
            return t;
          }
        }
      }
      return value;
    }

    @Override public String toString() {
      return "Suppliers.memoize(" + delegate + ")";
    }

    private static final long serialVersionUID = 0;
  }

Could someone explain what does that comment mean?

"value" does not need to be volatile; visibility piggy-backs on volatile read of "initialized".

How can volatile on "initialized" field affect "value" field? According to this article we can get inconsistent combination of "initialized" and "value" fields (eg. true+null). Am I wrong?

like image 475
nukie Avatar asked May 07 '15 08:05

nukie


1 Answers

The sentence basically means that the reads & writes of value are ordered in a certain way with the volatile reads & writes that guarantees that the written value will be visible to the read.


(Simplified) proof

If the value is not initialized, the program will execute those two statements:

value = t;          //normal write
initialized = true; //volatile write

and if the value is initialized, the program will execute those two statements:

if(!initialized) { ... } //volatile read
return value;            //normal read

Thanks to volatile semantics, you have a happens-before relationship between the volatile write and the volatile read and the normal read in return value is guaranteed to see the write at value = t. This works because the normal write is before the volatile write and the normal read is after the volatile read.


Why the order is important

For example if the program was written like this:

initialized = true;
value = t;

return value could return a null value because the write here does not happen before the volatile write which acts as a memory barrier and therefore it does not benefit from the volatile semantics any more.

like image 167
assylias Avatar answered Nov 13 '22 14:11

assylias