Java ReentrantReadWriteLock — правильное использование?

У меня возникают некоторые проблемы с параллелизмом в моем веб-приложении, где выполняется запись в БД, а также могут быть одновременные чтения. При записи сначала удаляются все строки, а затем вставляются новые, поэтому есть вероятность, что чтение будет выполняться, когда база данных пуста, что приведет к ошибке. Я использую ReentrantReadWriteLock, но хочу убедиться, что использую его правильно. Любые исправления будут оценены.

private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

public static Schedule lookupSchedule()
{
    lock.readLock().lock();
    try
    {
        // read data from the database, manipulate it, return it
    }
    finally
    {
       lock.readLock().unlock();
    }
}

public static void setSchedule()
{
    Connection conn = DB.getConnection();
    lock.writeLock.lock();
    try
    {
       conn.openTransaction();
       // delete all the rows from the table
       // insert all the new rows into the table
       conn.committTransaction();
    }
    catch (Exception ex)
    {
       conn.rollbackTransaction();
    }
    finally
    {
        lock.writeLock.unlock();
    }
}

person bluedevil2k    schedule 21.05.2014    source источник
comment
Этот взгляд прямо на меня. Как вы перерабатываете свое соединение?   -  person Peter Lawrey    schedule 21.05.2014
comment
Соединение закрывается в методах commitTransaction() или rollbackTransaction() и исходит из пула БД.   -  person bluedevil2k    schedule 21.05.2014
comment
Возможно, вы захотите переместить получение соединения с базой данных внутри вашей блокировки записи, поскольку похоже, что вы настроили его таким образом для метода поиска. В противном случае вы могли бы получить lookupSchedule() блокировку чтения, а затем, прежде чем он получит соединение с базой данных, группа писателей может захватить все соединения с базой данных, а затем заблокировать блокировку записи, что приведет к взаимоблокировке.   -  person azurefrog    schedule 21.05.2014
comment
Зачем ты вообще используешь замки? Свойства ACID базы данных должны гарантировать, что читатели используют любые данные в базе данных перед транзакцией записи, даже когда писатель работает...   -  person Alexandros    schedule 21.05.2014
comment
Также см.: stackoverflow.com/questions/11043712/ Теоретически вы можете наблюдать поведение фантомного чтения... Возможно, вам просто нужно настроить уровень изоляции.   -  person Alexandros    schedule 21.05.2014
comment
Блокировка записи является эксклюзивной, что означает, что во время выполнения setSchedule чтение не происходит, что, в свою очередь, означает, что lookupSchedule никогда не должен видеть пустую таблицу (если только setSchedule не вставляет строки). Что-то кроме блокировки не совсем то...   -  person vanOekel    schedule 22.05.2014


Ответы (1)


Что касается вопроса: ваше использование ReentrantReadWriteLock правильно.

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

Надеюсь это поможет...

Объяснение уровней изоляции в другом потоке SO: в чем разница между неповторяемое чтение и фантомное чтение?

Глава руководства по Java, посвященная уровням изоляции: http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html#transactions_data_integrity

person Alexandros    schedule 21.05.2014
comment
Я бы подумал, что транзакционный характер обновления также защитит его, но на практике я не видел этого. Уровень изоляции для этих транзакций установлен на TRANSACTION_READ_UNCOMMITTED, который, судя по вашим комментариям, необходимо изменить на TRANSACTION_READ_COMMITTED, чтобы это работало правильно без блокировок. - person bluedevil2k; 21.05.2014
comment
Точно; пожалуйста, попробуйте установить уровень изоляции и сообщите нам, сработало ли это для вас. Кроме того, как я упоминал ранее, при правильной изоляции вы можете просто выполнять транзакции и даже не беспокоиться о блокировке ReentrantReadWriteLock. БД должна позаботиться об этом за вас (даже если у вас несколько авторов). - person Alexandros; 21.05.2014
comment
Согласно учебнику по Java, кажется, что только TRANSACTION_SERIALIZABLE предотвратит фантомное чтение (см. Таблицу в главе, на которую есть ссылка в ответе). Я надеюсь, что смогу с этим жить... Это самый дорогой вариант, но я думаю, что только он позволит достичь того, чего вы хотите. Вы можете захотеть создать новые данные в другой таблице, а затем переместить их в исходную таблицу на стороне сервера (сохраненный процесс), чтобы уменьшить диапазон транзакции (или какой-то другой подобный трюк). - person Alexandros; 21.05.2014
comment
С уровнем изоляции TRANSACTION_READ_COMMITTED метод lookupSchedule никогда не должен видеть пустую таблицу, поскольку удаление и вставка находятся в одной транзакции, т. е. нет момента времени, когда lookupSchedule увидит пустую таблицу (поскольку удаление никогда не фиксируется без вставки, а незафиксированные изменения никогда не видны). от lookupSchedule). Я не думаю, что TRANSACTION_SERIALIZABLE здесь поможет. - person vanOekel; 22.05.2014