首页 > 解决方案 > 弱引用和自刷新缓存管理器

问题描述

抱歉,问题很长,我需要介绍环境,否则您可能会误解我的问题。

当前状态

我有一个缓存管理器< K, V >,对于给定的类 K 对象,返回一个由类型 V 参数化的持有者,表示在 Web 服务上与相应 K 关联的值。

持有者

Holder 类管理下一次获取的获取、同步和调度,因为缓存是为多个并行调用而设计的。Web 服务获取的数据有一个到期日期(在标头中提供),之后持有者可以再次获取它并再次安排自己为下一次到期。我有 3 个类(用于列表、地图和其他),但它们都以相同的方式使用。Holder<V>类有5个方法,2个直接访问,3个IoC访问

通常,这使我可以在足够的时间内并行处理多个数据流,例如

Stream.of(1l, 2l, 3l).parallel().map(cache::getPrice).mapToInt(p->p.copy().price).min();

或在 javafx 中制作更复杂的绑定,例如,当价格取决于您要购买的商品数量时

自我调度

Holder 类包含一个 SelfScheduling<V> 对象,它负责实际获取数据,放入 holder 并在数据过期后重新调度自身。

SelfScheduling 使用缓存中的 ScheduledExecutorService 来调度它自己的 fetch() 方法。它首先在 0 毫秒后自行调度,如果出错则在 10 秒后重新调度,或者如果获取新数据则在到期后重新调度。它可以暂停、恢复、在创建时启动,也可以停止。

这是我要修改的行为。如果持有人未在代码中的任何地方使用,我希望自我执行者在到期时从缓存中删除持有人

缓存管理器

仅供参考,我的缓存管理器包含一个 Map< K、Holder< V > > cachedPrices 来保存缓存数据,以及一个 getPrice(K) 方法,如果持有人丢失,则在缓存上同步,如果需要则创建持有人(双检查以避免不必要的同步),并返回持有人。

全球代码

这是我的代码的示例

public class CacheExample {

    public static class Holder<T>{

        SimpleObjectProperty<T> data = new SimpleObjectProperty<>();

        // real code removed

        T copy() {
            return null;
        }

        Observable asObservable() {
            return null;
        }

        void follow(ChangeListener<? super T> listener) {

        }

    }

    public static class SelfScheduled implements Runnable {

        // should use enum
        private Object state = "start";

        public void schedule(long ms) {
            // check state, sync, etc.
        }

        @Override
        public void run() {
            long next = fetch();
            schedule(next);
        }

        public long fetch() {
            // set the value in the holder

            // return the next expiry
            return 0;
        }

    }

    public Map<Long, Holder<Object>> cachePrices = new HashMap<>();

    public Holder<Object> getPrice(long param) {
        Holder<Object> ret = cachePrices.get(param);
        if (ret == null) {
            // sync, re check, etc.
            synchronized (cachePrices) {
                ret = cachePrices.get(param);
                if (ret == null) {
                    ret = new Holder<>();
                    // should be the fetch() call instead of null
                    makeSchedule(ret.data, null);
                }
            }
        }
        return ret;
    }

    public void makeSchedule(SimpleObjectProperty<Object> data, Runnable run) {
        // code removed.
        // creates a selfscheduler with fetch method and the data to store the
        // result.
    }

}

预期的修改

正如我上面写的,我想修改缓存在内存中保存数据的方式。特别是,当这些数据不再使用时,我认为没有理由维护大量的自调度实体来获取数据。如果到期时间为 5 秒(某些网络服务是),并且我缓存了 1000 个数据(这是一个非常低的值),那么这意味着我将无缘无故地每秒进行 200 次 fetch()。

我期望的是,当不再使用持有者时,自我调度会自行停止,而不是获取数据,它实际上会从缓存中删除持有者。例子 :

Holder< Price > p = cache.getPrice(1);
// here if the fetch() is called it should fetch the data
p.copy().price;
// now the price is no more used, on next fetch() it should remove p from the cache.
// If that happens, and later I re enter that code, the holder and the selfscheduler will be re created.
Holder< Price > p2 = cache.getPrice(22);
mylist.add(p2);
// now there is a strong reference to this price, so the fetch() method will keep scheduling the selfscheduler
// until mylist is no more strongly referenced.

不正确

然而,我对适当技术的了解在该领域是有限的。据我了解,我应该在缓存管理器和自我调度中使用弱引用来知道何时不再强烈引用持有者(通常,通过检查引用是否变为空来启动 fetch(),在这种情况下停止); 但是,这会导致持有者在下一次到期之前被 GC,这是我不想要的:一些数据的到期时间很长,并且仅用于简单的方法,例如 cache.getShopLocation() 不应该被 GC就在使用 copy() 返回的值之后。

因此,此代码不正确:

public class CacheExampleIncorrect {

    public static class Holder<T>{

        SimpleObjectProperty<T> data = new SimpleObjectProperty<>();

        // real code removed

        T copy() {
            return null;
        }

        Observable asObservable() {
            return null;
        }

        void follow(ChangeListener<? super T> listener) {

        }

    }

    public static class SelfScheduled<T> implements Runnable {

        WeakReference<Holder<T>> holder;

        Runnable onDelete;

        public void schedule(long ms) {
            // check state, sync, etc.
        }

        @Override
        public void run() {
            Holder<T> h = holder.get();
            if (h == null) {
                onDelete.run();
                return;
            }
            long next = fetch(h);
            schedule(next);
        }

        public long fetch(Holder<T> h) {
            // set the value in the holder

            // return the next expiry
            return 0;
        }

    }

    public Map<Long, WeakReference<Holder<Object>>> cachePrices = new HashMap<>();

    public Holder<Object> getPrice(long param) {
        WeakReference<Holder<Object>> h = cachePrices.get(param);
        Holder<Object> ret = h == null ? null : h.get();
        if (h == null) {
            synchronized (cachePrices) {
                h = cachePrices.get(param);
                ret = h == null ? null : h.get();
                if (ret == null) {
                    ret = new Holder<>();
                    h = new WeakReference<>(ret);
                    // should be the fetch() call instead of null
                    SelfScheduled<Object> sched = makeSchedule(h, null);
                    cachePrices.put(param, h);
                    // should be synced on cachedprice
                    sched.onDelete = () -> cachePrices.remove(param);
                }
            }
        }
        return ret;
    }

    public <T> SelfScheduled<T> makeSchedule(WeakReference<Holder<Object>> h, Runnable run) {
        // creates a selfscheduler with fetch method and the data to store the
        // result.
        return null;
    }

}

标签: javaweb-servicescachingjavafxweak-references

解决方案


推荐阅读