番石榴:MemoizingSupplier线程安全

nuk*_*kie 6 java concurrency guava

番石榴供应商级包含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;
  }
Run Code Online (Sandbox Code Playgroud)

有人可以解释这个评论意味着什么吗?

"价值"不需要波动; 可见性背驮式对"初始化"的易失性读取.

"初始化"字段的易变性如何影响"价值"字段?根据这篇文章,我们可以得到"initialized"和"value"字段的不一致组合(例如,true + null).我错了吗?

ass*_*ias 5

该句子基本上意味着读取和写入value以特定方式排序,具有易失性读取和写入,保证写入的值对读取可见.


(简体)证明

如果未初始化该值,程序将执行这两个语句:

value = t;          //normal write
initialized = true; //volatile write
Run Code Online (Sandbox Code Playgroud)

如果值已初始化,程序将执行这两个语句:

if(!initialized) { ... } //volatile read
return value;            //normal read
Run Code Online (Sandbox Code Playgroud)

由于易失性语义,您在易失性写入和易失性读取之间存在一个事先发生的关系,并且正常读入return value保证看到写入value = t.这是有效的,因为正常写入易失性写入之前,而正常读取易失性读取之后.


为什么订单很重要

例如,如果程序是这样编写的:

initialized = true;
value = t;
Run Code Online (Sandbox Code Playgroud)

return value 可以返回一个空值,因为这里的写操作不会发生在作为内存屏障的易失性写入之前,因此它不再受益于易失性语义.