Java 8 是否为供应商提供了高速缓存支持?
番石榴库有自己的供应商,
它不扩展Java 8供应商
。此外,番石榴还为供应商提供了缓存 - 供应商#memoize
。
有没有类似的东西,但对于Java 8供应商?
番石榴库有自己的供应商,
它不扩展Java 8供应商
。此外,番石榴还为供应商提供了缓存 - 供应商#memoize
。
有没有类似的东西,但对于Java 8供应商?
没有用于记忆的内置Java函数,尽管实现它并不难,例如,如下所示:
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
AtomicReference<T> value = new AtomicReference<>();
return () -> {
T val = value.get();
if (val == null) {
val = value.updateAndGet(cur -> cur == null ?
Objects.requireNonNull(delegate.get()) : cur);
}
return val;
};
}
请注意,存在不同的实现方法。如果备忘录中的供应商同时从不同的线程请求多次,则上述实现可能会多次调用委托。有时,这种实现比与锁的显式同步更可取。如果首选锁定,则可以使用 DCL:
public static <T> Supplier<T> memoizeLock(Supplier<T> delegate) {
AtomicReference<T> value = new AtomicReference<>();
return () -> {
T val = value.get();
if (val == null) {
synchronized(value) {
val = value.get();
if (val == null) {
val = Objects.requireNonNull(delegate.get());
value.set(val);
}
}
}
return val;
};
}
另请注意,正如注释中正确提到的@LouisWasserman,您可以使用方法参考轻松地将JDK供应商转换为番石榴供应商,反之亦然:
java.util.function.Supplier<String> jdkSupplier = () -> "test";
com.google.common.base.Supplier<String> guavaSupplier = jdkSupplier::get;
java.util.function.Supplier<String> jdkSupplierBack = guavaSupplier::get;
因此,在番石榴和JDK功能之间切换并不是一个大问题。
最简单的解决方案是
public static <T> Supplier<T> memoize(Supplier<T> original) {
ConcurrentHashMap<Object, T> store=new ConcurrentHashMap<>();
return ()->store.computeIfAbsent("dummy", key->original.get());
}
但是,最简单的并不总是最有效的。
如果你想要一个干净高效的解决方案,求助于一个匿名的内部类来保持可变状态将得到回报:
public static <T> Supplier<T> memoize1(Supplier<T> original) {
return new Supplier<T>() {
Supplier<T> delegate = this::firstTime;
boolean initialized;
public T get() {
return delegate.get();
}
private synchronized T firstTime() {
if(!initialized) {
T value=original.get();
delegate=() -> value;
initialized=true;
}
return delegate.get();
}
};
}
这使用委托供应商,该供应商将执行第一次操作,然后用无条件返回第一次评估捕获结果的供应商替换自身。由于它具有字段语义,因此可以无条件返回它,而无需任何其他同步。final
在方法中,仍然需要一个标志,因为在初始化期间并发访问的情况下,多个线程可能会在方法的入口处等待,然后再替换委托。因此,这些线程需要检测初始化是否已经完成。所有后续访问都将读取新的委托供应商并快速获得价值。synchronized
firstTime()
initialized