Блокировка ключа кэша без блокировки всего кэша - PullRequest
1 голос
/ 14 апреля 2010

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

Retrieving User [Bob]
Retrieving User [Bob]
Retrieving User [Bob]
Returned [Bob] ...caching
Returned [Bob] ...caching
Returned [Bob] ...caching 

Я хотел бы, чтобы первый запрос вызывал службу пользователя, в то время как два других запроса блокировались - и когда первый запрос возвращается, а затем кэширует объект, остальные два запроса проходят:

Retrieving User [Bob]
blocking...
blocking...
Returned [Bob] ...caching
[Bob] found in cache
[Bob] found in cache

Я думал о блокировке строки "Bob" (потому что из-за интернирования это всегда один и тот же объект, верно?). Будет ли это работать? И если да, то как мне отслеживать ключи, которые на самом деле существуют в кэше, и создавать вокруг них механизм блокировки, который затем возвращал бы действительный объект после его извлечения. Спасибо.

Ответы [ 3 ]

4 голосов
/ 14 апреля 2010

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

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

ReadWriteLock trial = new ReentrantReadWriteLock(fair);
ReadWriteLock lock = locks.putIfAbsent(key, trial);
if (lock == null) {
  /* The current thread won the race to create lock for key. */
  lock = trial;
}

(Использование ReadWriteLock необязательно; с его помощью вы можете сделать что-то необычное, например, разрешить одновременное чтение кэшированного значения несколькими потоками, но при этом другой поток может получить эксклюзивную блокировку, когда значение необходимо обновить.)

Может быть дорого создавать много блокировок, которые в конечном итоге будут отброшены, поскольку блокировка уже существует. Или вы можете использовать старую среду выполнения без java.util.concurrent. В таких случаях вы можете синхронизировать данные на карте:

Object lock;
synchronized (locks) {
  if (locks.containsKey(key))
    lock = locks.get(key);
  else {
    lock = new Object();
    locks.put(key, object);
  }
}
2 голосов
/ 14 апреля 2010

Я думал о блокировке строки "Bob" (потому что из-за интернирования это всегда один и тот же объект, верно?). Будет ли это работать?

Ранее я пробовал это, и на самом деле это не работает так, как вы ожидаете . Вы должны сначала убедиться, что вы звоните intern() в строке; однако синхронизация по внутренним строкам на самом деле очень плохая идея.

0 голосов
/ 14 апреля 2010

Если вы кешируете с картой, то блокировка на ключе сделает то, что вы предлагаете.

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