前段时间,我创建了一个Java类,它将延迟初始化封装到LazyObect
中。它维护并发性和线程安全性,并提供reset()
方法来清除值。
public final class LazyObject<T> {
private volatile T value;
private volatile boolean updated;
private final Supplier<T> supplier;
private LazyObject(Supplier<T> supplier) {
this.supplier = supplier;
}
public T get() {
if (!updated) {
synchronized(this) {
if (!updated) {
value = supplier.get();
updated = true;
}
}
}
return value;
}
public void reset() {
if (updated) {
synchronized(this) {
if (updated) {
updated = false;
value = null;
}
}
}
}
public static <B> LazyObject<B> forSupplier(Supplier<B> supplier) {
return new LazyObject<B>(supplier);
}
}
我想尝试将这个概念放得更远一点。如果LazyObject
持有一个仅在短时间内使用的非常大的对象(如内存密集型的hashmap),我希望在一段时间不使用后销毁该值。如果它最后再次使用,那么它将再次初始化并重新安排另一个销毁周期。
所以我创建了ExpirableLazyObject
。
public final class ExpirableLazyObject<T> {
private final LazyObject<T> value;
private final ScheduledThreadPoolExecutor executor;
private final long expirationDelay;
private final TimeUnit timeUnit;
private volatile ScheduledFuture<?> scheduledRemoval;
public ExpirableLazyObject(Supplier<T> supplier, ScheduledThreadPoolExecutor executor, long expirationDelay, TimeUnit timeUnit) {
value = LazyObject.forSupplier(() -> supplier.get());
this.expirationDelay = expirationDelay;
this.executor = executor;
this.timeUnit = timeUnit;
}
public T get() {
if (scheduledRemoval != null) {
scheduledRemoval.cancel(true);
}
T returnVal = value.get();
scheduledRemoval = executor.schedule(() -> value.reset(), expirationDelay, timeUnit);
return returnVal;
}
}
需要Supplier
和一些需要的参数来安排销毁值。每次调用get()
时,它都会延迟破坏。当然,我可以设计强制客户端通过GC处理值的创建和销毁,但我喜欢在内部管理实例的API。
好处是我可以持久保存缓存对象以支持操作,并且我可以懒得且定期自动刷新参数。
然而,我无法摆脱get()
方法可能有竞争条件的感觉,但我无法找出确切的原因。我不知道我是否需要一些同步块或者我是否正确识别原子性。但是我为安抚我的担忧而制定的每个同步块都会大大破坏并发性或引入新的竞争条件。我能看到防止任何竞争条件(如果有的话)的唯一方法是同步整个方法。但这会破坏并发性。这里真的有问题吗?
更新 已经确定存在竞争条件。我想我对如何解决这个问题有一些想法,但我希望听到任何可以有效实现这一目标并最大化并发性的建议。