Ehcache & многопоточность: как заблокировать при вставке в кеш? - PullRequest
0 голосов
/ 02 ноября 2018

Предположим, у меня есть многопоточное приложение с 4 потоками, которые совместно используют один (Eh) кэш; в кеше хранятся объекты UserProfile, чтобы каждый раз не извлекать их из базы данных.

Теперь, скажем, все эти 4 потока запрашивают один и тот же UserProfile с ID = 123 в одно и то же время - и он еще не был кэширован. Что нужно сделать, это сделать запрос к базе данных и вставить полученный объект UserProfile в кеш, чтобы его можно было использовать позже.

Однако я хочу добиться того, чтобы только один из этих потоков (первый) запрашивал базу данных и обновлял кэш, а остальные 3 ожидают (очередь) ее завершения ... и затем получают UserProfile. объект с ID = 123 прямо из кеша.

Как вы обычно реализуете такой сценарий? Использование блокировки / транзакций Ehcache ? Или скорее через что-то подобное? (Псевдо-код)

public UserProfile getUserProfile(int id) {
    result = ehcache.get(id)
    if (result == null) {  // not cached yet
        synchronized {  // queue threads
            result = ehcache.get(id)
            if (result == null) {  // is current thread the 1st one?
                result = database.fetchUserProfile(id)
                ehcache.put(id, result)
            }
        }
    }
    return result
}

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

Это называется Громовое стадо проблема.

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

Вы можете сделать 2 вещи. Одним из них является использование CacheLoaderWriter . Он загрузит отсутствующую запись и выполнит блокировку с правильной гранулярностью. Это самое простое решение, даже если вам нужно реализовать загрузчик-писатель.

Альтернатива более сложная. Вам нужен какой-то алгоритм блокировки строк. Например, вы можете сделать что-то вроде этого:

private final ReentrantLock locks = new ReentrantLocks[1024];
{
    for(int i = 0; i < locks.length; i)) {
        locks[i] = new ReentrantLock();
    }
}

public UserProfile getUserProfile(int id) {
    result = ehcache.get(id)
    if (result == null) {  // not cached yet
        ReentrantLock lock = locks[id % locks.length];
        lock.lock();
        try {
            result = ehcache.get(id)
            if (result == null) {  // is current thread the 1st one?
                result = database.fetchUserProfile(id)
                ehcache.put(id, result)
            }                
        } finally {
            lock.unlock();
        }
    }
    return result
}
0 голосов
/ 02 ноября 2018

Использовать обычную блокировку объекта Java:

private static final Object LOCK = new Object();


synchronized (LOCK) {

    result = ehcache.get(id);

    if ( result == null || ehcache.isExpired() ) {  

        // cache is expired or null so going to DB

        result = database.fetchUserProfile(id);

        ehcache.put(id, result)
    }

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