Повторяющаяся синхронизация блокировки чтения и записи на isWriteLocked

В настоящее время я ищу реализацию java-класса диспетчера сеансов, который предоставляет функциональные возможности для чтения и обновления токенов сеанса по запросу. Если токен сеанса обновляется (т. Е. Извлекается с сервера), то считыватели токена сеанса должны блокироваться до завершения обновления. Точно так же запрос обновления блокируется до тех пор, пока все текущие чтения не будут завершены. Поскольку запросы на чтение токена сеанса довольно часты по сравнению с запросами на обновление токена сеанса, я решил использовать ReentrantReadWriteLock для достижения синхронизации между чтением и обновлением. Вот как это выглядит:

  String refreshToken() {
         try{
             if (readWriteLock.writeLock().trylock()) {
                 //fetch session from server and store on disk
             }
        } finally {
              readWriteLock.writeLock().unlock();  
        }
        return readToken();
    }

   String readToken() {
         try {
         readWriteLock.readLock().lock();
         //read token from disk
         } finally {
               readWriteLock.readLock().unlock();
         }
    return token;
    }

}

Моя первая попытка состояла в том, чтобы использовать tryLock () для блокировки записи, чтобы, если она уже заблокирована для записи, тогда tryLock () вернет false, а затем получит блокировку чтения и блокировку до тех пор, пока блокировка записи не будет снята, тем самым перепланировав блокировку чтения темы для завершения чтения. Эта логика хорошо работает в случае, когда несколько потоков одновременно вызывают refreshSession (), тем самым позволяя только одному потоку инициировать обновление токена сеанса, в то время как все остальные потоки не выдерживают и блокируют чтение.

Однако приведенная выше логика не сработает, если поток только что получил блокировку чтения (путем вызова readToken ()), а другой поток вызывает refreshToken () для получения блокировки записи - в этом случае tryLock () завершится с ошибкой, пропустив запрос обновления, поскольку результат.

В качестве альтернативы я рассматривал метод readWriteLock.isWriteLocked (), который проверяет, получил ли какой-либо поток блокировку записи:

String refreshToken() {
    try{
        if (!readWriteLock.isWriteLocked()) {
            readWriteLock.writeLock().lock();
            //fetch session from server and store on disk
        } finally{
              readWriteLock.writeLock().unlock();  
        }
    }
    return readToken();
}

Однако у меня нет большого опыта работы с этим методом и я не совсем уверен в последствиях синхронизации, которые он будет иметь, поскольку я хочу убедиться, что только один поток может получить блокировку записи, а последующие запросы будут выполняться для чтения. Любые предложения / указатели будут очень признательны.


person user1623182    schedule 07.10.2015    source источник
comment
Если сделать refreshToken() метод synchronized, можно выполнить эту работу, тогда только один поток одновременно сможет получить доступ к refreshToken() части. Другие потоки будут ждать завершения того, который в настоящее время находится в методе.   -  person Westranger    schedule 08.10.2015
comment
Что ж, я хотел бы синхронизировать доступ к токену сеанса между читателями и писателями. Кроме того, в классе будут другие методы, поэтому я не хочу блокировать весь объект, синхронизируя этот метод. Отсюда и причина использования блокировки.   -  person user1623182    schedule 08.10.2015


Ответы (1)


Вы можете ввести там новые ReentrantLock и tryLock. ReentrantLock обещает только одного писателя и быстро терпит неудачу, если писатель существует.

ReentrantLock lock = new ReentrantLock();

public String refreshToken() {
    if (lock.tryLock()) {
        readWriteLock.writeLock().lock();
        try {
            // fetch session from server and store on disk
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
    return readToken();
}

Таким образом, только один поток когда-либо попытается написать. И никакие потоки не могут читать, пока идет запись.

person John Vint    schedule 08.10.2015
comment
Выглядит очень многообещающе - есть ли другие идеи, не требующие дополнительной блокировки? - person user1623182; 08.10.2015
comment
В отсутствие дальнейших комментариев / предложений я принимаю этот ответ. - person user1623182; 10.10.2015
comment
@ user1623182 Спасибо, что вы имели в виду в связи с вашим предыдущим комментарием? Без введения какой-либо формы мьютекса я не совсем понимаю, как вы можете достичь того, чего хотите. Например, ваш пример не будет защищать бесконечно, представьте, что два потока вызывают isWriteLocked одновременно, и оба успешно. В этом случае оба вызовут lock. - person John Vint; 12.10.2015
comment
Вы правы, у меня были такие же сомнения по поводу использования isWriteLocked напрямую, без использования каких-либо мьютексов. Однако я просто проверял, есть ли у вас какие-либо другие идеи / подходы, которые у вас есть, без использования двух замков. Считаете ли вы стратегию обновления сеанса, которую я принял, оптимальной с учетом требований API (как было изложено изначально?). - person user1623182; 13.10.2015
comment
Я имею в виду использование любых других примитивов или API параллелизма, о которых я, возможно, не знал. - person user1623182; 13.10.2015
comment
возвращаясь к этой проблеме после затишья, но видите ли вы состояние гонки в предложении выше для следующего сценария: когда два потока вызывают trylock () одновременно - person user1623182; 02.11.2015
comment
Единственный тип гонки, который я вижу, - это если readLock вызывается до writeLock. - person John Vint; 02.11.2015
comment
Извините, мой комментарий выше был наполовину готов. Сценарий, о котором я говорю: 1. Два потока T1 и T2 в конечном итоге вызывают tryLock () одновременно. 2. T1.tryLock () возвращает истину, и до того, как readWriteLock.writeLock (). Lock () сможет выполнить, T2.tryLock () завершится ошибкой и перейдет к readToken (). 3. T2.readToken () продолжает планироваться и ему удается получить readLock (), и прежде, чем он сможет снять блокировку, выполняется readWriteLock.writeLock (). Lock (), что вызывает нежелательное состояние гонки. Это очень редкий сценарий, но возможно ли это? - person user1623182; 03.11.2015