ReentrantReadWriteLock - много читателей за раз, один писатель за раз?

Я несколько новичок в многопоточных средах, и я пытаюсь найти лучшее решение для следующей ситуации:

Я читаю данные из базы данных один раз в день утром и сохраняю данные в HashMap в объекте Singleton. У меня есть метод setter, который вызывается только тогда, когда происходит внутридневное изменение БД (которое будет происходить 0-2 раза в день).

У меня также есть getter, который возвращает элемент на карте, и этот метод называется сотнями раз в день.

Я беспокоюсь о случае, когда геттер вызывается, пока я опустошаю и воссоздаю HashMap, пытаясь найти элемент в пустом/неправильном списке. Если синхронизировать эти методы, это предотвратит одновременное обращение двух читателей к геттеру, что может быть узким местом производительности. Я не хочу брать слишком много хитов, поскольку записи происходят так редко. Если я использую ReentrantReadWriteLock, это заставит очередь называть кого-либо, вызывающего геттер, до тех пор, пока не будет освобождена блокировка записи? Позволяет ли нескольким читателям одновременно получать доступ к получателю? Будет ли он применять только одного автора за раз?

Является ли кодирование это просто вопросом...

private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock read = readWriteLock.readLock();
private final Lock write = readWriteLock.writeLock();

public HashMap getter(String a) {
    read.lock();
    try {
        return myStuff_.get(a);            
    } finally {
        read.unlock();
    }
}

public void setter() 
{
    write.lock();
    try {
        myStuff_ = // my logic
     } finally {
          write.unlock();
    }
}

Ответ 1

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

private volatile Map<String,HashMap> myStuff_ = new HashMap<String,HashMap>();

public HashMap getter(String a) {
    return myStuff_.get(a);
}

public synchronized void setter() {
    // create a copy from the original
    Map<String,HashMap> copy = new HashMap<String,HashMap>(myStuff_);
    // populate the copy
    // replace copy with the original
    myStuff_ = copy;
}

При этом читатели полностью параллельны, и единственным штрафом, который они платят, является неустойчивое чтение на myStuff_ (что очень мало). Авторы синхронизированы для обеспечения взаимного исключения.

Ответ 2

Да, если блокировка записи удерживается потоком, тогда другие потоки, обращающиеся к методу геттера, будут блокироваться, так как они не могут получить блокировку чтения. Так что ты здесь прекрасна. Для получения дополнительной информации прочитайте JavaDoc ReentrantReadWriteLock - http://download.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html

Ответ 3

Вы пинаете это в начале дня... вы будете обновлять его 0-2 раз в день, и вы читаете его 100 раз в день. Предполагая, что чтение будет выполнено, скажем, 1 полная секунда (время ожидания) в течение 8 часов (28800 секунд), вы все еще получаете очень низкую нагрузку на чтение. Глядя на документы для ReentrantReadWriteLock, вы можете "tweek" режим, чтобы он был "справедливым", а это означает, что поток, ожидающий самого длинного, получит блокировку. Поэтому, если вы решите, что это справедливо, я не думаю, что ваши нити для записи будут истощены.

Ссылки

ReentrantReadWriteLock