Использование ReentrantReadWriteLock и логического флага - PullRequest
0 голосов
/ 07 декабря 2010

У меня есть кэш, который загружается заранее с большим количеством данных (фоновым потоком) и не может использоваться до полного заполнения (он также будет перезагружаться время от времени и будет непригодным для использования во время этой загрузки).Я хочу, чтобы классы, использующие его, проверяли флаг isLoaded() перед доступом.Я использую ReentrantReadWriteLock (я опускаю это в коде для простоты) для управления доступом, как это:

public class Cache {

   private volatile boolean loaded = false; //starts false

   private static String[] cache;

   private static Lock readLock;
   private static Lock writeLock;

   public Object get(Object key) {
       if (!readLock.tryLock()) throw IllegalStateException(...);
       try {
           ... do some work
       } finally {
           readLock.unlock();
       }
   }

   // called by background thread
   private void loadFull() {
      loaded = false;
      writeLock.lock()
      try {
          cache = new String[];
          ... fill cache
      } finally {
          writeLock.unlock();
          loaded = true;
      }
   }
....
}  

Теперь в моем другом классе у меня есть блок, подобный этому:

if (cache.isLoaded()) {
    try {
      Object x = cache.get(y);
    } catch (IllegalStateException iex) {
      // goto database for object
    }
} else {
    // goto database for object
}

Мне действительно нужно try/catch?Возможно ли когда-нибудь, чтобы флаг был установлен в ложь, и попытка readLock () не удалась?Должен ли я вообще беспокоиться о флаге и просто поймать исключение (поскольку я в основном делаю тот же код, если исключение выдается, как если бы флаг был ложным).Я просто чувствую, что делаю что-то не так, но не могу понять, как это сделать.Спасибо.

Ответы [ 3 ]

2 голосов
/ 07 декабря 2010

Мне действительно нужно попробовать / поймать?Возможно ли когда-нибудь установить флаг на false и попытка readLock () завершится неудачей?

Да, вам это нужно.Между вызовами cache.isLoaded() и cache.get() может войти писатель и получить блокировку записи - в этом случае cache.isLoaded() вернет true, но cache.get() сгенерирует исключение.

Должен ли я вообще беспокоиться о флаге и просто поймать исключение (поскольку я в основном выполняю тот же код, если исключение выдается, как если бы флаг был ложным).показали, что исключение выдается только в тех случаях, когда get не может получить блокировку чтения.Получение блокировки чтения завершается неудачно только в том случае, если в это время одновременно работает запись.isLoaded также возвращает false именно в этом сценарии.Поэтому достаточно полагаться на исключение.Также рассмотрите возможность создания специализированной CacheStaleException.

1 голос
/ 07 декабря 2010

tryLock завершится ошибкой, если какой-то другой поток уже получил эту блокировку. Это обычно означает, что исключение будет выдано, если клиенту не удастся получить блокировку из-за высокой конкуренции (несколько клиентов получают доступ к одному кешу) Существует ли какая-либо резервная стратегия, реализованная вами на уровне клиента, которая имеет дело с такими ситуациями?

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

0 голосов
/ 07 декабря 2010

Нет, но, честно говоря, ваша парадигма сбивает с толку. Предположительно дорого обращаться к реальной базе данных, и это является целью кеша. В случае, если кэш перезагружается, не лучше ли подождать, пока он не будет?

Если вы действительно хотите перейти в базу данных, если блокировка чтения не доступна сразу, я бы сделал это:

   public Object get(Object key) {
       Object returnValue;
       if (readLock.tryLock()) {
           try {
               ... do some work
               returnValue = ...
           } finally {
               readLock.unlock();
           }
       } else {
           //go to database
           returnValue = ...
       }
       return returnValue;
   }
...