Параллелизм — доступ к БД

#java #concurrency #thread-safety #java.util.concurrent

#java #параллелизм #потокобезопасность #java.util.concurrent

Вопрос:

Мне нужно обработать извлечение ожидающих записей и обновить их до состояния «InProcess» как единицу работы. Я хотел бы убедиться, что приведенный ниже код поддерживает параллелизм, а другие потоки ждут, пока мой текущий поток не будет обработан. Каков наилучший способ добиться этого?

 public Collection<Object> processPendingMessages() {
    Collection<Object> messages = null;
    //Retrieve pending messages
    messages = messageDAO.getPendingMessages(Direction.INBOUND);
    //Update pending messages to Inprocess
    if (messages!=null amp;amp; messages.size()>0) {
        messageDAO.updateMessagesToInprocess(messages); 
    } 
    return messages;
}
  

Мы высоко ценим ваш вклад.
Спасибо.

Ответ №1:

Вы не хотите обрабатывать параллелизм DBAccess только на уровне jvm с помощью (синхронизированный). В вашем случае доступ осуществляется к базе данных, что будет невозможно при простой синхронизации на уровне jvm. Вам также необходимо обработать параллелизм на уровне подключения к базе данных следующим образом. Вы не упомянули, какая реализация находится на вашем уровне DAO в том, что касается DBaccess, но что бы это ни было, следующие уровни изоляции для JDBC должны иметь смысл и для вашей реализации, независимо от того, какая она (JDBC, JPA с гибернацией или EJB и т.д.). Ознакомьтесь с приведенными ниже уровнями изоляции и определите, какой из них подходит для вашего приложения. Всего наилучшего в вашей реализации.

Уровни изоляции транзакций определяют, какие данные видны операторам в транзакции как единице транзакции. Эти уровни напрямую влияют на уровень параллельного доступа, определяя, какое взаимодействие возможно между транзакциями с одним и тем же целевым источником данных.

АНОМАЛИИ БАЗЫ ДАННЫХ

Аномалии базы данных — это сгенерированные результаты, которые кажутся неверными при рассмотрении в рамках одной транзакции, но являются правильными при рассмотрении в рамках всех транзакций. Различные типы аномалий базы данных описаны следующим образом:

Грязные чтения происходят, когда: Транзакция A вставляет строку в таблицу. Транзакция B считывает новую строку. Транзакция A откатывается. Транзакция B, возможно, выполнила работу с системой на основе строки, вставленной транзакцией A, но эта строка никогда не становилась постоянной частью базы данных.

Неповторяемые чтения происходят, когда: Транзакция A считывает строку. Транзакция B изменяет строку. Транзакция A считывает ту же строку во второй раз и получает новые результаты.

Фантомные чтения происходят, когда: Транзакция A считывает все строки, которые удовлетворяют предложению WHERE в SQL-запросе. Транзакция B вставляет дополнительную строку, которая удовлетворяет предложению WHERE. Транзакция A повторно оценивает условие WHERE и выбирает дополнительную строку.

УРОВНИ ИЗОЛЯЦИИ, КОТОРЫЕ ОБРАБАТЫВАЮТ КАЖДУЮ ИЗ ВЫШЕПЕРЕЧИСЛЕННЫХ АНОМАЛИЙ

JDBC_TRANSACTION_NONE Это специальная константа, указывающая на то, что драйвер JDBC не поддерживает транзакции.

JDBC_TRANSACTION_READ_UNCOMMITTED Этот уровень позволяет транзакциям видеть незафиксированные изменения в данных. На этом уровне возможны все аномалии базы данных.

JDBC_TRANSACTION_READ_COMMITTED любые изменения, внесенные внутри транзакции, не видны за ее пределами, пока транзакция не будет зафиксирована. Это предотвращает возможность грязного чтения.

Прочитанные строки JDBC_TRANSACTION_REPEATABLE_READ сохраняют блокировки, чтобы другая транзакция не могла их изменить, когда транзакция не завершена. Это запрещает грязные чтения и неповторяемые чтения. Фантомное чтение все еще возможно.

Таблицы JDBC_TRANSACTION_SERIALIZABLE заблокированы для транзакции, так что условия WHERE не могут быть изменены другими транзакциями, которые добавляют значения в таблицу или удаляют значения из нее. Это предотвращает все типы аномалий базы данных.

Метод setTransactionIsolation может использоваться для изменения уровня изоляции транзакции для соединения.

Комментарии:

1. Спасибо! Именно к этому все меня и привело. Благодарю за разъяснение.

Ответ №2:

вы должны позволить базе данных обрабатывать параллелизм.

в вашем DAO вы можете выполнить sql-запрос:

 // works on oracle.
// query may differ depending on vendor
select * from test where id=? for update 
  

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

Комментарии:

1. Спасибо, я тоже об этом думаю. Слишком много читая о параллелизме, я не был уверен, оставлять ли это JVM или внедрять диспетчер транзакций. Ценю ваши комментарии.

Ответ №3:

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

Комментарии:

1. Извините, это не сработает, как только код будет развернут в кластере (а это происходит всегда).

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