Обновите блокировку чтения Java для записи блокировки кэширования в Map - PullRequest
2 голосов
/ 03 февраля 2011

У меня тупиковая ситуация в приведенном ниже коде:

private static final ReadWriteLock opClassesLock = new ReentrantReadWriteLock();
private static final Map<Class<?>, ServiceClass> opClasses = new WeakHashMap<Class<?>, ServiceClass>();
public static ServiceClass get(Class<?> myClass) {
    opClassesLock.readLock().lock();
    try {
        ServiceClass op = opClasses.get(myClass);
        if (op == null) {
            opClassesLock.writeLock().lock(); // deadlock here
            try {
                op = new ServiceClass(myClass);
                opClasses.put(myClass, op);
            } finally {
                opClassesLock.writeLock().unlock();
            }
        }
        return op;
    } finally {
        opClassesLock.readLock().unlock();
    }
}

Если бы я проверил документацию на ReentrantReadWriteLock, я мог бы предсказать это:

Вход в систему также позволяетпонижение уровня блокировки записи до блокировки чтения путем получения блокировки записи, затем блокировки чтения и затем освобождения блокировки записи. Однако обновление с блокировки чтения до блокировки записи невозможно.

Кроме того, что используется только одна блокировка вместо блокировки чтения / записи (которая не позволяетодновременное чтение), есть ли другой способ решить эту проблему?

Ответы [ 3 ]

3 голосов
/ 03 февраля 2011

Используйте хорошо протестированное решение, такое как

new MapMaker().weakKeys().makeMap();

из Гуава . Вы даже можете делать такие вещи, как

new MapMaker().weakKeys()
.concurrencyLevel(16)
.expireAfterAccess(5, TimeUnit.MINUTES) 
.maximumSize(1000)
.makeComputingMap(new Function<Class<?>, ServiceClass>() {
    @Override
    public ServiceClass apply(Class<?> myClass) {
        return new ServiceClass(myClass);
    }
});

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


Причиной тупика является блокировка записи, когда оба потока удерживают блокировку чтения. В отличие от понижения блокировки, обновление может блокировать. Сначала нужно снять блокировку чтения.


Когда вы получаете блокировку записи, вы должны проверить, не выполнял ли другой поток эту работу.

0 голосов
/ 28 ноября 2013

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

0 голосов
/ 03 февраля 2011

Теперь я понимаю, что приведенный выше пример, если бы он работал, все равно не был бы действительным, потому что это могло бы произойти

  • Поток 1 видит, что на карте нет записи, и получает блокировку записи
  • Поток 2 видит, что на карте нет записи, и получает блокировку записи (но должен ждать)
  • Поток 1 добавляет запись и снимает блокировку записи
  • Поток 2теперь добавляет запись, но она уже есть

Итак, есть два варианта:

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

  2. Если запись может быть сделана дважды (если это просто кэш) снимите блокировку чтения перед получением блокировки записи.

...