base.Suppliers
Lazy thread-sefe sigleton supplier 遅延で、スレッドセーフで、シングルトンなオブジェクトを作る時に使います。
遅延でシングルトンならこういうコードを書いたことがあると思います。
ただし、これはスレッドセーフではありません。同時に呼ばれると、generateCache
中に、何度もcacheが生成されてしまいます。
public Cache getCache(){
if(cahe==null){
cache=generateCache();
}
return cache;
}
これを、スレッドセーフにしたのが、Suppliersです。Supplierインターフェースを使います。
compose
Functionをを組み合わせて別のsupplierから別の値を返すようにします。
memoize
一度しかget()を呼び出さないsupplierを生成します。
よく間違えるのですが、基本memoizeは初期化時に一度呼ぶだけで、後は、生成された、Supplierのget()を使って値を取り出します。
memoizeWithExpiration
有効期限を指定可能です。簡易なキャッシュとしても使えます。 特に、Cacheが対応していないGWTとかで
ofInstance
常に一定値を返すSupplierを生成します。
supplierFunction
supplierから、値を取り出すFunctionを生成します。
synchronizedSupplier
引数のsupplierをスレッドセーフにします。
supplierをimplementsしただけのクラスをスレッド対応にしたい時に使います。
中身
ダブルチェックロッキングを使って実現しています。
これは、JDK1.4まではbadノウハウでしたが、1.5以降は、violateの効果で正常に動くようです。 via wiki
@VisibleForTesting
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;
}
例
!=nullの代わりに使う
まあ、スレッドセーフにする必要性がないので、使う必要もないのだけど
public static class ImageUrlDataResizeInfo implements Supplier<Anchor>{
private Supplier<Anchor> downloadLink=Suppliers.memoize(this);
@Override
public Anchor get() {
Anchor a=generateDownloadLink();
return a;
}
public Anchor getDownloadLink(){
return downloadLink.get();
}
}
Google App Engineのキャッシュ生成時に
普通にcache==null
で初期化してると、初期化中にアクセスがあると二度呼ばれる危険がある。
ただし、nullもキャッシュしてしまうので、nullの場合は失敗ということで、NullPointerException
呼び出して、次回に持ち越しにしてもらう。
public static Cache getCache() throws NullPointerException{
return memorized.get();
}
public static Supplier<Cache> cacheSupplier=new Supplier<Cache>(){
public Cache get(){
CacheFactory cacheFactory;
Cache cache=null;
try {
@SuppressWarnings("rawtypes")
Map props = new HashMap();
props.put(GCacheFactory.EXPIRATION_DELTA, 3600*24);// expire 1 days cache is always new
cacheFactory = CacheManager.getInstance().getCacheFactory();
cache = cacheFactory.createCache(props);
System.out.println("generate-cache");
} catch (CacheException e) {
e.printStackTrace();
}
if(cache==null){
throw new NullPointerException("cache is null");
}
return cache;
}
};
private static volatile Supplier<Cache> memorized = Suppliers.memoize(cacheSupplier);