Слабая справка и самообновление кеш-менеджера - PullRequest
0 голосов
/ 20 сентября 2018

Извините за длинный вопрос, мне нужно представить окружение, иначе вы можете неправильно понять мою проблему.

Текущее состояние

У меня есть менеджер кэша , который дляданный объект класса K возвращает держатель, параметризованный типом V, представляющий значение, связанное с веб-службой, соответствующему K.

Holder

Классы Holder управляют извлечением, синхронизацией,и планирование следующей выборки, потому что кэш предназначен для нескольких параллельных вызовов.Данные, извлеченные веб-службой, имеют дату истечения срока действия (указана в заголовке), после чего владелец может извлечь их снова и снова планирует для следующего истечения срока действия.У меня есть 3 класса (для списка, карты и других), но все они используются одинаково.Класс Holder имеет 5 методов, 2 для прямого доступа и 3 для доступа IoC

  • void waitData () ожидает, пока данные не будут выбраны хотя бы один раз.Внутренне is использует countdownlatch.
  • V copy () ожидает выборки данных хотя бы один раз, затем возвращает копию кэшированного V. Простые элементы возвращаются, как они есть, в то время как более сложные (например, Mapцены в данном магазине, на которые ссылается идентификатор мебели), копируются в синхронизированном цикле (чтобы избежать другого извлечения () для повреждения данных)
  • void follow (JavaFX.Listener ) регистрирует нового слушателяV будет уведомлено об изменениях в данных владельца.Если владелец уже получил данные, слушатель уведомляется об этих данных, как будто они новые.
  • void unfollow (JavaFX.Listener ) отменяет регистрацию предварительно зарегистрированного слушателя.
  • Наблюдаемый asObservable() возвращает наблюдаемый.Это позволяет использовать его, например, в графическом интерфейсе javafx.

Как правило, это позволяет мне выполнять такие вещи, как потоковая передача нескольких данных параллельно с достаточным временем, например

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

или выполнятьГораздо более сложные привязки в javafx, например, когда цена зависит от количества предметов, которые вы хотите приобрести

Self Scheduling

Класс держателя содержит объект SelfScheduling , который отвечает зана самом деле получить данные, поместить их в держатель и перепланировать себя после истечения срока действия данных.

SelfScheduling использует ScheduledExecutorService в кэше, чтобы запланировать свой собственный метод fetch ().Она начинается с планирования самого себя через 0 мс, повторного планирования через 10 с в случае ошибки или после истечения срока действия, если были получены новые данные.Он может быть приостановлен, возобновлен, запущен при создании и может быть остановлен.

Это поведение, которое я хочу изменить. Я хочу, чтобы самоисполнитель удалил Holder из кэшапо истечении срока, если держатель не используется нигде в коде

Диспетчер кэша

Просто для информации, мой диспетчер кэша состоит из Map > cachedPrices для храненияданные кеша и метод getPrice (K), который синхронизирует кеш, если держатель отсутствует, создайте держатель, если требуется (двойная проверка, чтобы избежать ненужной синхронизации), и верните держатель.

Global Code

Вот пример того, как выглядит мой код

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 с (некоторые веб-сервисы 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.

Неверно

Однако мои знания адекватных технологий ограничены в этой области.Насколько я понял, я должен использовать слабую ссылку в диспетчере кеша и самопланирование, чтобы знать, когда на держатель больше нет сильных ссылок (обычно запускают fetch (), проверяя, стала ли ссылка нулевой, и в этом случае просто остановите);Однако это приведет к тому, что держатель будет GC'd ДО следующего истечения срока, что мне не нужно: некоторые данные имеют очень длительный срок действия и используются только в простом методе, например cache.getShopLocation () не должен быть GC'dсразу после использования значения, возвращенного функцией 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;
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...