Ленивая загрузка эталонной реализации - PullRequest
11 голосов
/ 23 сентября 2011

Находясь под впечатлением от вычислительной карты Guava , я ищу своего рода "вычислительную ссылку" - ленивую загрузочную эталонную реализацию, которая упрощает использование параллельной Guava, под которой я подразумеваю, что она обрабатывает все блокировки, загрузка и обработка исключений под капотом, только разоблачая метод get().

После того, как краткий поиск ничего не дал, я быстро свернул свой собственный в качестве подтверждения концепции:

public abstract class ComputingRef<T> implements Callable<T> {

   private volatile T referent = null;
   private Lock lock = new ReentrantLock();

   public T get() {
      T temp = referent;
      if (temp == null) {
         lock.lock();
         try {
            temp = referent;
            if (temp == null) {
               try {
                  referent = temp = call();
               }
               catch (Exception e) {
                  if (e instanceof RuntimeException) {
                     throw (RuntimeException)e;
                  }
                  else {
                     throw new RuntimeException(e);
                  }
               }
            }
         }
         finally {
            lock.unlock();
         }
      }
      return temp;
   }
}

Это ComputingRef может быть анонимно расширено для реализации call(), который функционирует как фабричный метод:

ComputingRef<MyObject> lazySingletonRef = new ComputingRef<MyObject>() {
   @Override
   public MyObject call() {
      //fetch MyObject from database and return
   }
};

Я не удовлетворен тем, что эта реализация является оптимальной, но она демонстрирует, что япосле.

Позже я нашел этот пример из T2 Framework , который выглядит более сложным.

Теперь мои вопросы:

  • Как можно улучшить мой приведенный выше код?
  • Как он соотносится с примером T2 и какие преимущества дает его сложность?
  • Существуют ли другие реализации?что такое ленивая ссылка на загрузку, которую я пропустил в своем поиске?

РЕДАКТИРОВАТЬ: Обновил мою реализацию, чтобы использовать локальную переменную, как предложено @ ответом irreputable - пожалуйста, подпишите его, если вы считаете приведенный выше пример полезным.

Ответы [ 3 ]

23 голосов
/ 23 сентября 2011

См. Suppliers.memoize(Supplier) для ленивой инициализации значения.

2 голосов
/ 23 сентября 2011

Во всяком случае, вот как я это сделаю (а потом я буду беспокоиться о производительности позже):

public abstract class ComputingRef<T> implements Callable<T> {

    private final AtomicReference<T> ref = new AtomicReference<T>();

    public T get() {
        if (ref.get() == null) {
            try {
                final T newValue = call();
                if (ref.compareAndSet(null, newValue)) {
                    return newValue;
                }
            } catch (final Exception e) {
                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new RuntimeException(e);
                }
            }

        }
        return ref.get();
    }
}

Единственное «препятствие» при таком подходе состоит в том, что существует условие гонки, которое может привести кнесколько экземпляров ссылочного объекта (особенно, если ComputingRef совместно используется большим количеством потоков, которые одновременно достигают get()).Если создание экземпляра референтного класса стоит слишком дорого или вы хотите избежать множественного создания любой ценой, то я бы также выбрал двойную проверку блокировки.

Вы также должны убедиться, что референтный объектубирает за собой.В противном случае, если compareAndSet() не удастся выполнить, обязательно выполните необходимую очистку.

(Обратите внимание, что если референт должен быть одноэлементным, то я бы использовал инициализацию по требованию идиомы владельца запроса). вместо.)

2 голосов
/ 23 сентября 2011

Это старая добрая проверенная идиома блокировки.Вы должны добавить локальную переменную для производительности.В вашем impl, у вас есть 2 энергозависимых чтения в быстром пути (когда референт установлен).Чек http://en.wikipedia.org/wiki/Double-checked_locking

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