弱引用和自刷新缓存管理器

时间:2018-09-20 13:17:01

标签: java web-services caching javafx weak-references

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

当前状态

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

持有人

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

  • void waitData()等待直到至少提取一次数据。在内部使用计数倒计时。
  • V copy()等待至少提取一次数据,然后返回缓存的V的副本。简单项目将按原样返回,但更为复杂(例如,由参考引用的给定商店中的价格映射家具ID)复制到一个同步循环中(以避免另一个fetch()破坏数据)
  • void follow(JavaFX.Listener )注册一个新的V侦听器,以在对持有人的数据进行修改时得到通知。如果持有者已经接收到数据,则该数据将被通知给侦听器,就像它是新数据一样。
  • void unfollow(JavaFX.Listener )取消注册先前注册的侦听器。
  • Observable asObservable()返回一个Observable。这样就可以在javafx GUI中使用。

通常,这使我可以在足够的时间内并行执行多个数据流之类的操作,例如

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

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

自排程

holder类包含一个SelfScheduling 对象,该对象负责实际获取数据,将其放入holder中,并在数据过期后自行重新计划。

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

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

缓存管理器

仅作参考之用,我的缓存管理器由一个Map > 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.
    }

}

预期的修改

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

我期望的是,当不再使用Holder时,自调度会自行停止,而不是获取数据,而是将其从缓存中删除。例子:

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.

不正确

但是,我对适当技术的了解仅限于该领域。据我了解,我应该在缓存管理器和自调度中使用一个弱引用,以了解何时不再对持有者进行更强的引用(通常,通过检查引用是否为null来启动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;
    }

}

0 个答案:

没有答案